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 "modest-progress-bar.h"
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include "hildon/hildon-pannable-area.h"
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
69 #define MYDOCS_ENV "MYDOCSDIR"
70 #define DOCS_FOLDER ".documents"
72 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
73 struct _ModestMsgViewWindowPrivate {
76 GtkWidget *main_scroll;
77 GtkWidget *find_toolbar;
80 /* Progress observers */
81 GSList *progress_widgets;
84 GtkWidget *prev_toolitem;
85 GtkWidget *next_toolitem;
86 gboolean progress_hint;
88 /* Optimized view enabled */
89 gboolean optimized_view;
91 /* Whether this was created via the *_new_for_search_result() function. */
92 gboolean is_search_result;
94 /* Whether the message is in outbox */
97 /* A reference to the @model of the header view
98 * to allow selecting previous/next messages,
99 * if the message is currently selected in the header view.
101 const gchar *header_folder_id;
102 GtkTreeModel *header_model;
103 GtkTreeRowReference *row_reference;
104 GtkTreeRowReference *next_row_reference;
106 gulong clipboard_change_handler;
107 gulong queue_change_handler;
108 gulong account_removed_handler;
109 gulong row_changed_handler;
110 gulong row_deleted_handler;
111 gulong row_inserted_handler;
112 gulong rows_reordered_handler;
115 GtkWidget *remove_attachment_banner;
122 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
123 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
124 static void modest_header_view_observer_init(
125 ModestHeaderViewObserverIface *iface_class);
126 static void modest_msg_view_window_finalize (GObject *obj);
127 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
129 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
130 ModestMsgViewWindow *obj);
131 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
132 ModestMsgViewWindow *obj);
134 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
136 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
137 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
141 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
143 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
144 gboolean show_toolbar);
146 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
148 ModestMsgViewWindow *window);
150 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
153 ModestMsgViewWindow *window);
155 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
157 ModestMsgViewWindow *window);
159 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
160 GtkTreePath *tree_path,
161 GtkTreeIter *tree_iter,
162 ModestMsgViewWindow *window);
164 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
168 ModestMsgViewWindow *window);
170 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
172 const gchar *tny_folder_id);
174 static void on_queue_changed (ModestMailOperationQueue *queue,
175 ModestMailOperation *mail_op,
176 ModestMailOperationQueueNotification type,
177 ModestMsgViewWindow *self);
179 static void on_account_removed (TnyAccountStore *account_store,
183 static void on_move_focus (GtkWidget *widget,
184 GtkDirectionType direction,
187 static void view_msg_cb (ModestMailOperation *mail_op,
194 static void set_progress_hint (ModestMsgViewWindow *self,
197 static void update_window_title (ModestMsgViewWindow *window);
199 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
200 static void init_window (ModestMsgViewWindow *obj);
202 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
204 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
206 static gboolean on_fetch_image (ModestMsgView *msgview,
209 ModestMsgViewWindow *window);
211 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
212 GtkScrollType scroll_type,
215 static gboolean message_reader (ModestMsgViewWindow *window,
216 ModestMsgViewWindowPrivate *priv,
218 GtkTreeRowReference *row_reference);
220 /* list my signals */
227 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
228 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
231 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
232 MODEST_TYPE_MSG_VIEW_WINDOW, \
233 ModestMsgViewWindowPrivate))
235 static GtkWindowClass *parent_class = NULL;
237 /* uncomment the following if you have defined any signals */
238 static guint signals[LAST_SIGNAL] = {0};
241 modest_msg_view_window_get_type (void)
243 static GType my_type = 0;
245 static const GTypeInfo my_info = {
246 sizeof(ModestMsgViewWindowClass),
247 NULL, /* base init */
248 NULL, /* base finalize */
249 (GClassInitFunc) modest_msg_view_window_class_init,
250 NULL, /* class finalize */
251 NULL, /* class data */
252 sizeof(ModestMsgViewWindow),
254 (GInstanceInitFunc) modest_msg_view_window_init,
257 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
258 "ModestMsgViewWindow",
261 static const GInterfaceInfo modest_header_view_observer_info =
263 (GInterfaceInitFunc) modest_header_view_observer_init,
264 NULL, /* interface_finalize */
265 NULL /* interface_data */
268 g_type_add_interface_static (my_type,
269 MODEST_TYPE_HEADER_VIEW_OBSERVER,
270 &modest_header_view_observer_info);
276 save_state (ModestWindow *self)
278 modest_widget_memory_save (modest_runtime_get_conf (),
280 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
284 restore_settings (ModestMsgViewWindow *self)
288 conf = modest_runtime_get_conf ();
289 modest_widget_memory_restore (conf,
291 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
294 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
295 GtkScrollType scroll_type,
299 ModestMsgViewWindowPrivate *priv;
300 gboolean return_value;
302 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
303 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
308 add_scroll_binding (GtkBindingSet *binding_set,
310 GtkScrollType scroll)
312 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
314 gtk_binding_entry_add_signal (binding_set, keyval, 0,
316 GTK_TYPE_SCROLL_TYPE, scroll,
317 G_TYPE_BOOLEAN, FALSE);
318 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
320 GTK_TYPE_SCROLL_TYPE, scroll,
321 G_TYPE_BOOLEAN, FALSE);
325 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
327 GObjectClass *gobject_class;
328 ModestWindowClass *modest_window_class;
329 GtkBindingSet *binding_set;
331 gobject_class = (GObjectClass*) klass;
332 modest_window_class = (ModestWindowClass *) klass;
334 parent_class = g_type_class_peek_parent (klass);
335 gobject_class->finalize = modest_msg_view_window_finalize;
337 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
338 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
339 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
341 modest_window_class->save_state_func = save_state;
343 klass->scroll_child = modest_msg_view_window_scroll_child;
345 signals[MSG_CHANGED_SIGNAL] =
346 g_signal_new ("msg-changed",
347 G_TYPE_FROM_CLASS (gobject_class),
349 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
351 modest_marshal_VOID__POINTER_POINTER,
352 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
354 signals[SCROLL_CHILD_SIGNAL] =
355 g_signal_new ("scroll-child",
356 G_TYPE_FROM_CLASS (gobject_class),
357 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
358 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
360 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
361 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
363 binding_set = gtk_binding_set_by_class (klass);
364 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
365 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
366 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
367 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
368 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
369 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
371 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
375 static void modest_header_view_observer_init(
376 ModestHeaderViewObserverIface *iface_class)
378 iface_class->update_func = modest_msg_view_window_update_model_replaced;
382 modest_msg_view_window_init (ModestMsgViewWindow *obj)
384 ModestMsgViewWindowPrivate *priv;
385 ModestWindowPrivate *parent_priv = NULL;
386 GtkActionGroup *action_group = NULL;
387 GError *error = NULL;
388 GdkPixbuf *window_icon;
390 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
391 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
392 parent_priv->ui_manager = gtk_ui_manager_new();
394 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
395 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
397 /* Add common actions */
398 gtk_action_group_add_actions (action_group,
399 modest_action_entries,
400 G_N_ELEMENTS (modest_action_entries),
402 gtk_action_group_add_toggle_actions (action_group,
403 msg_view_toggle_action_entries,
404 G_N_ELEMENTS (msg_view_toggle_action_entries),
407 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
408 g_object_unref (action_group);
410 /* Load the UI definition */
411 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
414 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
415 g_error_free (error);
420 /* Add accelerators */
421 gtk_window_add_accel_group (GTK_WINDOW (obj),
422 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
424 priv->is_search_result = FALSE;
425 priv->is_outbox = FALSE;
427 priv->msg_view = NULL;
428 priv->header_model = NULL;
429 priv->header_folder_id = NULL;
430 priv->clipboard_change_handler = 0;
431 priv->queue_change_handler = 0;
432 priv->account_removed_handler = 0;
433 priv->row_changed_handler = 0;
434 priv->row_deleted_handler = 0;
435 priv->row_inserted_handler = 0;
436 priv->rows_reordered_handler = 0;
437 priv->progress_hint = FALSE;
439 priv->optimized_view = FALSE;
440 priv->purge_timeout = 0;
441 priv->remove_attachment_banner = NULL;
442 priv->msg_uid = NULL;
444 priv->sighandlers = NULL;
447 init_window (MODEST_MSG_VIEW_WINDOW(obj));
449 /* Set window icon */
450 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
452 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
453 g_object_unref (window_icon);
456 hildon_program_add_window (hildon_program_get_instance(),
463 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
465 ModestMsgViewWindowPrivate *priv = NULL;
467 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
469 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
471 set_progress_hint (self, TRUE);
477 set_progress_hint (ModestMsgViewWindow *self,
480 ModestWindowPrivate *parent_priv;
481 ModestMsgViewWindowPrivate *priv;
483 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
485 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
486 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
488 /* Sets current progress hint */
489 priv->progress_hint = enabled;
491 if (GTK_WIDGET_VISIBLE (self)) {
492 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
499 init_window (ModestMsgViewWindow *obj)
501 GtkWidget *main_vbox;
502 ModestMsgViewWindowPrivate *priv;
504 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
506 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
507 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
508 main_vbox = gtk_vbox_new (FALSE, 6);
509 #ifdef MODEST_TOOLKIT_HILDON2
510 priv->main_scroll = hildon_pannable_area_new ();
511 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
513 #ifdef MODEST_USE_MOZEMBED
514 priv->main_scroll = priv->msg_view;
515 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
517 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
518 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
520 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
521 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
522 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
525 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
526 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
528 priv->find_toolbar = hildon_find_toolbar_new (NULL);
529 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
530 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
532 gtk_widget_show_all (GTK_WIDGET(main_vbox));
536 modest_msg_view_window_disconnect_signals (ModestWindow *self)
538 ModestMsgViewWindowPrivate *priv;
539 ModestHeaderView *header_view = NULL;
540 ModestWindow *main_window = NULL;
542 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
544 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
545 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
546 priv->clipboard_change_handler))
547 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
548 priv->clipboard_change_handler);
550 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
551 priv->queue_change_handler))
552 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
553 priv->queue_change_handler);
555 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
556 priv->account_removed_handler))
557 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
558 priv->account_removed_handler);
560 if (priv->header_model) {
561 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
562 priv->row_changed_handler))
563 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
564 priv->row_changed_handler);
566 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
567 priv->row_deleted_handler))
568 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
569 priv->row_deleted_handler);
571 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
572 priv->row_inserted_handler))
573 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
574 priv->row_inserted_handler);
576 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
577 priv->rows_reordered_handler))
578 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
579 priv->rows_reordered_handler);
582 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
583 priv->sighandlers = NULL;
585 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
586 FALSE); /* don't create */
590 header_view = MODEST_HEADER_VIEW(
591 modest_main_window_get_child_widget(
592 MODEST_MAIN_WINDOW(main_window),
593 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
594 if (header_view == NULL)
597 modest_header_view_remove_observer(header_view,
598 MODEST_HEADER_VIEW_OBSERVER(self));
602 modest_msg_view_window_finalize (GObject *obj)
604 ModestMsgViewWindowPrivate *priv;
606 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
608 /* Sanity check: shouldn't be needed, the window mgr should
609 call this function before */
610 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
612 if (priv->header_model != NULL) {
613 g_object_unref (priv->header_model);
614 priv->header_model = NULL;
617 if (priv->remove_attachment_banner) {
618 gtk_widget_destroy (priv->remove_attachment_banner);
619 g_object_unref (priv->remove_attachment_banner);
620 priv->remove_attachment_banner = NULL;
623 if (priv->purge_timeout > 0) {
624 g_source_remove (priv->purge_timeout);
625 priv->purge_timeout = 0;
628 if (priv->row_reference) {
629 gtk_tree_row_reference_free (priv->row_reference);
630 priv->row_reference = NULL;
633 if (priv->next_row_reference) {
634 gtk_tree_row_reference_free (priv->next_row_reference);
635 priv->next_row_reference = NULL;
639 g_free (priv->msg_uid);
640 priv->msg_uid = NULL;
643 G_OBJECT_CLASS(parent_class)->finalize (obj);
647 select_next_valid_row (GtkTreeModel *model,
648 GtkTreeRowReference **row_reference,
652 GtkTreeIter tmp_iter;
654 GtkTreePath *next = NULL;
655 gboolean retval = FALSE, finished;
657 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
659 path = gtk_tree_row_reference_get_path (*row_reference);
660 gtk_tree_model_get_iter (model, &tmp_iter, path);
661 gtk_tree_row_reference_free (*row_reference);
662 *row_reference = NULL;
666 TnyHeader *header = NULL;
668 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
669 gtk_tree_model_get (model, &tmp_iter,
670 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
674 if (msg_is_visible (header, is_outbox)) {
675 next = gtk_tree_model_get_path (model, &tmp_iter);
676 *row_reference = gtk_tree_row_reference_new (model, next);
677 gtk_tree_path_free (next);
681 g_object_unref (header);
684 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
685 next = gtk_tree_model_get_path (model, &tmp_iter);
687 /* Ensure that we are not selecting the same */
688 if (gtk_tree_path_compare (path, next) != 0) {
689 gtk_tree_model_get (model, &tmp_iter,
690 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
693 if (msg_is_visible (header, is_outbox)) {
694 *row_reference = gtk_tree_row_reference_new (model, next);
698 g_object_unref (header);
702 /* If we ended up in the same message
703 then there is no valid next
707 gtk_tree_path_free (next);
709 /* If there are no more messages and we don't
710 want to start again in the first one then
711 there is no valid next message */
717 gtk_tree_path_free (path);
722 /* TODO: This should be in _init(), with the parameters as properties. */
724 modest_msg_view_window_construct (ModestMsgViewWindow *self,
725 const gchar *modest_account_name,
726 const gchar *msg_uid)
729 ModestMsgViewWindowPrivate *priv = NULL;
730 ModestWindowPrivate *parent_priv = NULL;
731 ModestDimmingRulesGroup *menu_rules_group = NULL;
732 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
733 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
735 obj = G_OBJECT (self);
736 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
737 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
739 priv->msg_uid = g_strdup (msg_uid);
742 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
743 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
744 gtk_widget_show (parent_priv->menubar);
745 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
747 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
748 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
749 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
751 /* Add common dimming rules */
752 modest_dimming_rules_group_add_rules (menu_rules_group,
753 modest_msg_view_menu_dimming_entries,
754 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
755 MODEST_WINDOW (self));
756 modest_dimming_rules_group_add_rules (toolbar_rules_group,
757 modest_msg_view_toolbar_dimming_entries,
758 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
759 MODEST_WINDOW (self));
760 modest_dimming_rules_group_add_rules (clipboard_rules_group,
761 modest_msg_view_clipboard_dimming_entries,
762 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
763 MODEST_WINDOW (self));
765 /* Insert dimming rules group for this window */
766 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
767 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
768 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
769 g_object_unref (menu_rules_group);
770 g_object_unref (toolbar_rules_group);
771 g_object_unref (clipboard_rules_group);
773 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
775 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);
776 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
777 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
778 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
779 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
780 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
781 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
782 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
783 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
784 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
785 G_CALLBACK (modest_ui_actions_on_details), obj);
786 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
787 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
788 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
789 G_CALLBACK (on_fetch_image), obj);
791 g_signal_connect (G_OBJECT (obj), "key-release-event",
792 G_CALLBACK (modest_msg_view_window_key_event),
795 g_signal_connect (G_OBJECT (obj), "key-press-event",
796 G_CALLBACK (modest_msg_view_window_key_event),
799 g_signal_connect (G_OBJECT (obj), "move-focus",
800 G_CALLBACK (on_move_focus), obj);
802 /* Mail Operation Queue */
803 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
805 G_CALLBACK (on_queue_changed),
808 /* Account manager */
809 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
811 G_CALLBACK(on_account_removed),
814 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
816 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
817 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
818 priv->last_search = NULL;
820 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
822 /* Init the clipboard actions dim status */
823 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
825 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
830 /* FIXME: parameter checks */
832 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
833 const gchar *modest_account_name,
834 const gchar *msg_uid,
836 GtkTreeRowReference *row_reference)
838 ModestMsgViewWindow *window = NULL;
839 ModestMsgViewWindowPrivate *priv = NULL;
840 TnyFolder *header_folder = NULL;
841 ModestHeaderView *header_view = NULL;
842 ModestWindow *main_window = NULL;
843 ModestWindowMgr *mgr = NULL;
846 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
849 mgr = modest_runtime_get_window_mgr ();
850 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
851 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
853 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
855 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
857 /* Remember the message list's TreeModel so we can detect changes
858 * and change the list selection when necessary: */
860 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
862 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
863 MODEST_MAIN_WINDOW(main_window),
864 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
867 if (header_view != NULL){
868 header_folder = modest_header_view_get_folder(header_view);
869 /* This could happen if the header folder was
870 unseleted before opening this msg window (for
871 example if the user selects an account in the
872 folder view of the main window */
874 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
875 priv->header_folder_id = tny_folder_get_id(header_folder);
876 g_assert(priv->header_folder_id != NULL);
877 g_object_unref(header_folder);
881 /* Setup row references and connect signals */
882 priv->header_model = g_object_ref (model);
885 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
886 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
887 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
889 priv->row_reference = NULL;
890 priv->next_row_reference = NULL;
893 /* Connect signals */
894 priv->row_changed_handler =
895 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
896 G_CALLBACK(modest_msg_view_window_on_row_changed),
898 priv->row_deleted_handler =
899 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
900 G_CALLBACK(modest_msg_view_window_on_row_deleted),
902 priv->row_inserted_handler =
903 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
904 G_CALLBACK(modest_msg_view_window_on_row_inserted),
906 priv->rows_reordered_handler =
907 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
908 G_CALLBACK(modest_msg_view_window_on_row_reordered),
911 if (header_view != NULL){
912 modest_header_view_add_observer(header_view,
913 MODEST_HEADER_VIEW_OBSERVER(window));
916 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
917 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
919 /* gtk_widget_show_all (GTK_WIDGET (window)); */
920 modest_msg_view_window_update_priority (window);
921 /* Check dimming rules */
922 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
923 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
924 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
926 return MODEST_WINDOW(window);
930 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
931 const gchar *modest_account_name,
932 const gchar *msg_uid,
933 GtkTreeRowReference *row_reference)
935 ModestMsgViewWindow *window = NULL;
936 ModestMsgViewWindowPrivate *priv = NULL;
937 TnyFolder *header_folder = NULL;
938 ModestWindowMgr *mgr = NULL;
942 mgr = modest_runtime_get_window_mgr ();
943 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
944 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
946 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
948 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
950 /* Remember the message list's TreeModel so we can detect changes
951 * and change the list selection when necessary: */
953 if (header_view != NULL){
954 header_folder = modest_header_view_get_folder(header_view);
955 /* This could happen if the header folder was
956 unseleted before opening this msg window (for
957 example if the user selects an account in the
958 folder view of the main window */
960 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
961 priv->header_folder_id = tny_folder_get_id(header_folder);
962 g_assert(priv->header_folder_id != NULL);
963 g_object_unref(header_folder);
967 /* Setup row references and connect signals */
968 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
971 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
972 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
973 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
975 priv->row_reference = NULL;
976 priv->next_row_reference = NULL;
979 /* Connect signals */
980 priv->row_changed_handler =
981 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
982 G_CALLBACK(modest_msg_view_window_on_row_changed),
984 priv->row_deleted_handler =
985 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
986 G_CALLBACK(modest_msg_view_window_on_row_deleted),
988 priv->row_inserted_handler =
989 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
990 G_CALLBACK(modest_msg_view_window_on_row_inserted),
992 priv->rows_reordered_handler =
993 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
994 G_CALLBACK(modest_msg_view_window_on_row_reordered),
997 if (header_view != NULL){
998 modest_header_view_add_observer(header_view,
999 MODEST_HEADER_VIEW_OBSERVER(window));
1002 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1004 path = gtk_tree_row_reference_get_path (row_reference);
1005 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1007 gtk_tree_model_get (priv->header_model, &iter,
1008 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1010 message_reader (window, priv, header, row_reference);
1012 gtk_tree_path_free (path);
1014 /* Check dimming rules */
1015 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1016 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1017 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1019 return MODEST_WINDOW(window);
1023 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1024 const gchar *modest_account_name,
1025 const gchar *msg_uid)
1027 ModestMsgViewWindow *window = NULL;
1028 ModestMsgViewWindowPrivate *priv = NULL;
1029 ModestWindowMgr *mgr = NULL;
1031 mgr = modest_runtime_get_window_mgr ();
1032 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1033 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1034 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1036 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1038 /* Remember that this is a search result,
1039 * so we can disable some UI appropriately: */
1040 priv->is_search_result = TRUE;
1042 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1044 update_window_title (window);
1045 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1046 modest_msg_view_window_update_priority (window);
1048 /* Check dimming rules */
1049 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1050 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1051 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1053 return MODEST_WINDOW(window);
1057 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1058 const gchar *modest_account_name,
1059 const gchar *msg_uid)
1061 GObject *obj = NULL;
1062 ModestMsgViewWindowPrivate *priv;
1063 ModestWindowMgr *mgr = NULL;
1065 g_return_val_if_fail (msg, NULL);
1066 mgr = modest_runtime_get_window_mgr ();
1067 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1068 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1069 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1070 modest_account_name, msg_uid);
1072 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1073 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1075 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1077 /* Check dimming rules */
1078 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1079 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1080 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1082 return MODEST_WINDOW(obj);
1086 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1089 ModestMsgViewWindow *window)
1091 check_dimming_rules_after_change (window);
1095 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1097 ModestMsgViewWindow *window)
1099 check_dimming_rules_after_change (window);
1101 /* The window could have dissapeared */
1104 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1106 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1107 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1111 /* On insertions we check if the folder still has the message we are
1112 * showing or do not. If do not, we do nothing. Which means we are still
1113 * not attached to any header folder and thus next/prev buttons are
1114 * still dimmed. Once the message that is shown by msg-view is found, the
1115 * new model of header-view will be attached and the references will be set.
1116 * On each further insertions dimming rules will be checked. However
1117 * this requires extra CPU time at least works.
1118 * (An message might be deleted from TnyFolder and thus will not be
1119 * inserted into the model again for example if it is removed by the
1120 * imap server and the header view is refreshed.)
1123 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1124 GtkTreePath *tree_path,
1125 GtkTreeIter *tree_iter,
1126 ModestMsgViewWindow *window)
1128 ModestMsgViewWindowPrivate *priv = NULL;
1129 TnyHeader *header = NULL;
1131 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1132 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1134 g_assert (model == priv->header_model);
1136 /* Check if the newly inserted message is the same we are actually
1137 * showing. IF not, we should remain detached from the header model
1138 * and thus prev and next toolbar buttons should remain dimmed. */
1139 gtk_tree_model_get (model, tree_iter,
1140 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1143 if (TNY_IS_HEADER (header)) {
1146 uid = modest_tny_folder_get_header_unique_id (header);
1147 if (!g_str_equal(priv->msg_uid, uid)) {
1148 check_dimming_rules_after_change (window);
1150 g_object_unref (G_OBJECT(header));
1154 g_object_unref(G_OBJECT(header));
1157 if (priv->row_reference) {
1158 gtk_tree_row_reference_free (priv->row_reference);
1161 /* Setup row_reference for the actual msg. */
1162 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1163 if (priv->row_reference == NULL) {
1164 g_warning("No reference for msg header item.");
1168 /* Now set up next_row_reference. */
1169 if (priv->next_row_reference) {
1170 gtk_tree_row_reference_free (priv->next_row_reference);
1173 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1174 select_next_valid_row (priv->header_model,
1175 &(priv->next_row_reference), FALSE, priv->is_outbox);
1177 /* Connect the remaining callbacks to become able to detect
1178 * changes in header-view. */
1179 priv->row_changed_handler =
1180 g_signal_connect (priv->header_model, "row-changed",
1181 G_CALLBACK (modest_msg_view_window_on_row_changed),
1183 priv->row_deleted_handler =
1184 g_signal_connect (priv->header_model, "row-deleted",
1185 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1187 priv->rows_reordered_handler =
1188 g_signal_connect (priv->header_model, "rows-reordered",
1189 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1192 check_dimming_rules_after_change (window);
1196 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1200 ModestMsgViewWindow *window)
1202 ModestMsgViewWindowPrivate *priv = NULL;
1203 gboolean already_changed = FALSE;
1205 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1207 /* If the current row was reordered select the proper next
1208 valid row. The same if the next row reference changes */
1209 if (priv->row_reference &&
1210 gtk_tree_row_reference_valid (priv->row_reference)) {
1212 path = gtk_tree_row_reference_get_path (priv->row_reference);
1213 if (gtk_tree_path_compare (path, arg1) == 0) {
1214 if (priv->next_row_reference) {
1215 gtk_tree_row_reference_free (priv->next_row_reference);
1217 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1218 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1219 already_changed = TRUE;
1221 gtk_tree_path_free (path);
1223 if (!already_changed &&
1224 priv->next_row_reference &&
1225 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1227 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1228 if (gtk_tree_path_compare (path, arg1) == 0) {
1229 if (priv->next_row_reference) {
1230 gtk_tree_row_reference_free (priv->next_row_reference);
1232 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1233 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1235 gtk_tree_path_free (path);
1237 check_dimming_rules_after_change (window);
1240 /* The modest_msg_view_window_update_model_replaced implements update
1241 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1242 * actually belongs to the header-view is the same as the TnyFolder of
1243 * the message of msg-view or not. If they are different, there is
1244 * nothing to do. If they are the same, then the model has replaced and
1245 * the reference in msg-view shall be replaced from the old model to
1246 * the new model. In this case the view will be detached from it's
1247 * header folder. From this point the next/prev buttons are dimmed.
1250 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1251 GtkTreeModel *model,
1252 const gchar *tny_folder_id)
1254 ModestMsgViewWindowPrivate *priv = NULL;
1255 ModestMsgViewWindow *window = NULL;
1257 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1258 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1260 window = MODEST_MSG_VIEW_WINDOW(observer);
1261 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1263 /* If there is an other folder in the header-view then we do
1264 * not care about it's model (msg list). Else if the
1265 * header-view shows the folder the msg shown by us is in, we
1266 * shall replace our model reference and make some check. */
1267 if(model == NULL || tny_folder_id == NULL ||
1268 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1271 /* Model is changed(replaced), so we should forget the old
1272 * one. Because there might be other references and there
1273 * might be some change on the model even if we unreferenced
1274 * it, we need to disconnect our signals here. */
1275 if (priv->header_model) {
1276 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1277 priv->row_changed_handler))
1278 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1279 priv->row_changed_handler);
1280 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1281 priv->row_deleted_handler))
1282 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1283 priv->row_deleted_handler);
1284 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1285 priv->row_inserted_handler))
1286 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1287 priv->row_inserted_handler);
1288 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1289 priv->rows_reordered_handler))
1290 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1291 priv->rows_reordered_handler);
1294 if (priv->row_reference)
1295 gtk_tree_row_reference_free (priv->row_reference);
1296 if (priv->next_row_reference)
1297 gtk_tree_row_reference_free (priv->next_row_reference);
1298 g_object_unref(priv->header_model);
1301 priv->row_changed_handler = 0;
1302 priv->row_deleted_handler = 0;
1303 priv->row_inserted_handler = 0;
1304 priv->rows_reordered_handler = 0;
1305 priv->next_row_reference = NULL;
1306 priv->row_reference = NULL;
1307 priv->header_model = NULL;
1310 priv->header_model = g_object_ref (model);
1312 /* Also we must connect to the new model for row insertions.
1313 * Only for insertions now. We will need other ones only after
1314 * the msg is show by msg-view is added to the new model. */
1315 priv->row_inserted_handler =
1316 g_signal_connect (priv->header_model, "row-inserted",
1317 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1320 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1321 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1325 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1327 ModestMsgViewWindowPrivate *priv= NULL;
1329 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1330 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1332 return priv->progress_hint;
1336 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1338 ModestMsgViewWindowPrivate *priv= NULL;
1340 TnyHeader *header = NULL;
1341 GtkTreePath *path = NULL;
1344 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1345 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1347 /* If the message was not obtained from a treemodel,
1348 * for instance if it was opened directly by the search UI:
1350 if (priv->header_model == NULL ||
1351 priv->row_reference == NULL ||
1352 !gtk_tree_row_reference_valid (priv->row_reference)) {
1353 msg = modest_msg_view_window_get_message (self);
1355 header = tny_msg_get_header (msg);
1356 g_object_unref (msg);
1361 /* Get iter of the currently selected message in the header view: */
1362 path = gtk_tree_row_reference_get_path (priv->row_reference);
1363 g_return_val_if_fail (path != NULL, NULL);
1364 gtk_tree_model_get_iter (priv->header_model,
1368 /* Get current message header */
1369 gtk_tree_model_get (priv->header_model, &iter,
1370 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1373 gtk_tree_path_free (path);
1378 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1380 ModestMsgViewWindowPrivate *priv;
1382 g_return_val_if_fail (self, NULL);
1384 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1386 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1390 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1392 ModestMsgViewWindowPrivate *priv;
1394 g_return_val_if_fail (self, NULL);
1396 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1398 return (const gchar*) priv->msg_uid;
1402 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1405 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1406 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1407 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1411 is_active = gtk_toggle_action_get_active (toggle);
1414 gtk_widget_show (priv->find_toolbar);
1415 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1417 gtk_widget_hide (priv->find_toolbar);
1418 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1421 /* update the toggle buttons status */
1422 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1423 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1427 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1428 ModestMsgViewWindow *obj)
1430 GtkToggleAction *toggle;
1431 ModestWindowPrivate *parent_priv;
1432 ModestMsgViewWindowPrivate *priv;
1434 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1435 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1437 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1438 gtk_toggle_action_set_active (toggle, FALSE);
1439 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1443 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1444 ModestMsgViewWindow *obj)
1446 gchar *current_search;
1447 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1449 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1450 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1454 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1456 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1457 g_free (current_search);
1458 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1462 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1464 g_free (priv->last_search);
1465 priv->last_search = g_strdup (current_search);
1466 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1469 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1470 g_free (priv->last_search);
1471 priv->last_search = NULL;
1473 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1474 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1477 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1478 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1479 g_free (priv->last_search);
1480 priv->last_search = NULL;
1482 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1483 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1487 g_free (current_search);
1492 modest_msg_view_window_get_zoom (ModestWindow *window)
1494 ModestMsgViewWindowPrivate *priv;
1496 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1498 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1499 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1503 modest_msg_view_window_key_event (GtkWidget *window,
1509 focus = gtk_window_get_focus (GTK_WINDOW (window));
1511 /* for the find toolbar case */
1512 if (focus && GTK_IS_ENTRY (focus)) {
1513 if (event->keyval == GDK_BackSpace) {
1515 copy = gdk_event_copy ((GdkEvent *) event);
1516 gtk_widget_event (focus, copy);
1517 gdk_event_free (copy);
1522 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1523 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1524 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1525 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1526 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1527 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1528 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1529 /* gboolean return_value; */
1531 if (event->type == GDK_KEY_PRESS) {
1532 GtkScrollType scroll_type;
1534 switch (event->keyval) {
1537 scroll_type = GTK_SCROLL_STEP_UP; break;
1540 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1542 case GDK_KP_Page_Up:
1543 scroll_type = GTK_SCROLL_PAGE_UP; break;
1545 case GDK_KP_Page_Down:
1546 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1549 scroll_type = GTK_SCROLL_START; break;
1552 scroll_type = GTK_SCROLL_END; break;
1553 default: scroll_type = GTK_SCROLL_NONE;
1556 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1557 /* scroll_type, FALSE, &return_value); */
1568 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1571 ModestMsgViewWindowPrivate *priv;
1572 GtkTreeIter tmp_iter;
1573 gboolean is_last_selected;
1575 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1576 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1578 /*if no model (so no rows at all), then virtually we are the last*/
1579 if (!priv->header_model || !priv->row_reference)
1582 if (!gtk_tree_row_reference_valid (priv->row_reference))
1585 path = gtk_tree_row_reference_get_path (priv->row_reference);
1589 is_last_selected = TRUE;
1590 while (is_last_selected) {
1592 gtk_tree_path_next (path);
1593 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1595 gtk_tree_model_get (priv->header_model, &tmp_iter,
1596 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1599 if (msg_is_visible (header, priv->is_outbox))
1600 is_last_selected = FALSE;
1601 g_object_unref(G_OBJECT(header));
1604 gtk_tree_path_free (path);
1605 return is_last_selected;
1609 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1611 ModestMsgViewWindowPrivate *priv;
1613 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1614 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1616 return priv->header_model != NULL;
1620 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1622 ModestMsgViewWindowPrivate *priv;
1624 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1625 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1627 return priv->is_search_result;
1631 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1633 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1635 if (!check_outbox) {
1638 ModestTnySendQueueStatus status;
1639 status = modest_tny_all_send_queues_get_msg_status (header);
1640 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1641 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1646 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1649 ModestMsgViewWindowPrivate *priv;
1650 gboolean is_first_selected;
1651 GtkTreeIter tmp_iter;
1653 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1654 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1656 /*if no model (so no rows at all), then virtually we are the first*/
1657 if (!priv->header_model || !priv->row_reference)
1660 if (!gtk_tree_row_reference_valid (priv->row_reference))
1663 path = gtk_tree_row_reference_get_path (priv->row_reference);
1667 is_first_selected = TRUE;
1668 while (is_first_selected) {
1670 if(!gtk_tree_path_prev (path))
1672 /* Here the 'if' is needless for logic, but let make sure
1673 * iter is valid for gtk_tree_model_get. */
1674 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1676 gtk_tree_model_get (priv->header_model, &tmp_iter,
1677 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1680 if (msg_is_visible (header, priv->is_outbox))
1681 is_first_selected = FALSE;
1682 g_object_unref(G_OBJECT(header));
1685 gtk_tree_path_free (path);
1686 return is_first_selected;
1691 GtkTreeRowReference *row_reference;
1695 message_reader_performer (gboolean canceled,
1697 GtkWindow *parent_window,
1698 TnyAccount *account,
1701 ModestMailOperation *mail_op = NULL;
1702 MsgReaderInfo *info;
1704 info = (MsgReaderInfo *) user_data;
1705 if (canceled || err) {
1709 /* Register the header - it'll be unregistered in the callback */
1710 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1712 /* New mail operation */
1713 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1714 modest_ui_actions_disk_operations_error_handler,
1717 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1718 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1719 g_object_unref (mail_op);
1721 /* Update dimming rules */
1722 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1723 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1726 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1727 g_object_unref (info->header);
1728 g_slice_free (MsgReaderInfo, info);
1733 * Reads the message whose summary item is @header. It takes care of
1734 * several things, among others:
1736 * If the message was not previously downloaded then ask the user
1737 * before downloading. If there is no connection launch the connection
1738 * dialog. Update toolbar dimming rules.
1740 * Returns: TRUE if the mail operation was started, otherwise if the
1741 * user do not want to download the message, or if the user do not
1742 * want to connect, then the operation is not issued
1745 message_reader (ModestMsgViewWindow *window,
1746 ModestMsgViewWindowPrivate *priv,
1748 GtkTreeRowReference *row_reference)
1750 ModestWindowMgr *mgr;
1751 TnyAccount *account;
1753 MsgReaderInfo *info;
1755 g_return_val_if_fail (row_reference != NULL, FALSE);
1757 mgr = modest_runtime_get_window_mgr ();
1758 /* Msg download completed */
1759 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1760 /* Ask the user if he wants to download the message if
1762 if (!tny_device_is_online (modest_runtime_get_device())) {
1763 GtkResponseType response;
1765 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1766 _("mcen_nc_get_msg"));
1767 if (response == GTK_RESPONSE_CANCEL)
1770 folder = tny_header_get_folder (header);
1771 info = g_slice_new (MsgReaderInfo);
1772 info->header = g_object_ref (header);
1773 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1775 /* Offer the connection dialog if necessary */
1776 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1778 TNY_FOLDER_STORE (folder),
1779 message_reader_performer,
1781 g_object_unref (folder);
1786 folder = tny_header_get_folder (header);
1787 account = tny_folder_get_account (folder);
1788 info = g_slice_new (MsgReaderInfo);
1789 info->header = g_object_ref (header);
1790 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1792 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1793 g_object_unref (account);
1794 g_object_unref (folder);
1800 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1802 ModestMsgViewWindowPrivate *priv;
1803 GtkTreePath *path= NULL;
1804 GtkTreeIter tmp_iter;
1806 gboolean retval = TRUE;
1807 GtkTreeRowReference *row_reference = NULL;
1809 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1810 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1812 if (!priv->row_reference)
1815 /* Update the next row reference if it's not valid. This could
1816 happen if for example the header which it was pointing to,
1817 was deleted. The best place to do it is in the row-deleted
1818 handler but the tinymail model do not work like the glib
1819 tree models and reports the deletion when the row is still
1821 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1822 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1823 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1824 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1827 if (priv->next_row_reference)
1828 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1832 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1834 gtk_tree_model_get_iter (priv->header_model,
1837 gtk_tree_path_free (path);
1839 gtk_tree_model_get (priv->header_model, &tmp_iter,
1840 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1843 /* Read the message & show it */
1844 if (!message_reader (window, priv, header, row_reference)) {
1847 gtk_tree_row_reference_free (row_reference);
1850 g_object_unref (header);
1856 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1858 ModestMsgViewWindowPrivate *priv = NULL;
1860 gboolean finished = FALSE;
1861 gboolean retval = FALSE;
1863 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1864 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1866 /* Return inmediatly if there is no header model */
1867 if (!priv->header_model || !priv->row_reference)
1870 path = gtk_tree_row_reference_get_path (priv->row_reference);
1871 while (!finished && gtk_tree_path_prev (path)) {
1875 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1876 gtk_tree_model_get (priv->header_model, &iter,
1877 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1881 if (msg_is_visible (header, priv->is_outbox)) {
1882 GtkTreeRowReference *row_reference;
1883 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1884 /* Read the message & show it */
1885 retval = message_reader (window, priv, header, row_reference);
1886 gtk_tree_row_reference_free (row_reference);
1890 g_object_unref (header);
1894 gtk_tree_path_free (path);
1899 view_msg_cb (ModestMailOperation *mail_op,
1906 ModestMsgViewWindow *self = NULL;
1907 ModestMsgViewWindowPrivate *priv = NULL;
1908 GtkTreeRowReference *row_reference = NULL;
1910 /* Unregister the header (it was registered before creating the mail operation) */
1911 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1913 row_reference = (GtkTreeRowReference *) user_data;
1915 gtk_tree_row_reference_free (row_reference);
1919 /* If there was any error */
1920 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1921 gtk_tree_row_reference_free (row_reference);
1925 /* Get the window */
1926 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1927 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1928 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1930 /* Update the row reference */
1931 if (priv->row_reference != NULL) {
1932 gtk_tree_row_reference_free (priv->row_reference);
1933 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1934 if (priv->next_row_reference != NULL) {
1935 gtk_tree_row_reference_free (priv->next_row_reference);
1937 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1938 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1941 /* Mark header as read */
1942 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1943 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1945 /* Set new message */
1946 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1947 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1948 modest_msg_view_window_update_priority (self);
1949 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1950 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1953 /* Set the new message uid of the window */
1954 if (priv->msg_uid) {
1955 g_free (priv->msg_uid);
1956 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1959 /* Notify the observers */
1960 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1961 0, priv->header_model, priv->row_reference);
1964 g_object_unref (self);
1965 gtk_tree_row_reference_free (row_reference);
1969 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1971 ModestMsgViewWindowPrivate *priv;
1973 TnyFolderType folder_type;
1975 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1977 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1979 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1983 folder = tny_msg_get_folder (msg);
1985 folder_type = modest_tny_folder_guess_folder_type (folder);
1986 g_object_unref (folder);
1988 g_object_unref (msg);
1996 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1998 ModestMsgViewWindowPrivate *priv;
1999 TnyHeader *header = NULL;
2000 TnyHeaderFlags flags = 0;
2002 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2004 if (priv->header_model && priv->row_reference) {
2006 GtkTreePath *path = NULL;
2008 path = gtk_tree_row_reference_get_path (priv->row_reference);
2009 g_return_if_fail (path != NULL);
2010 gtk_tree_model_get_iter (priv->header_model,
2012 gtk_tree_row_reference_get_path (priv->row_reference));
2014 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2016 gtk_tree_path_free (path);
2019 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2021 header = tny_msg_get_header (msg);
2022 g_object_unref (msg);
2027 flags = tny_header_get_flags (header);
2028 g_object_unref(G_OBJECT(header));
2031 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2036 toolbar_resize (ModestMsgViewWindow *self)
2038 ModestMsgViewWindowPrivate *priv = NULL;
2039 ModestWindowPrivate *parent_priv = NULL;
2041 gint static_button_size;
2042 ModestWindowMgr *mgr;
2044 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2045 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2046 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2048 mgr = modest_runtime_get_window_mgr ();
2049 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2051 if (parent_priv->toolbar) {
2052 /* left size buttons */
2053 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2054 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2055 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2056 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2057 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2058 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2059 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2060 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2061 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2062 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2063 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2064 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2065 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2066 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2067 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2068 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2070 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2071 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2072 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2073 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2079 modest_msg_view_window_show_toolbar (ModestWindow *self,
2080 gboolean show_toolbar)
2082 ModestMsgViewWindowPrivate *priv = NULL;
2083 ModestWindowPrivate *parent_priv;
2084 GtkWidget *reply_button = NULL, *menu = NULL;
2085 const gchar *action_name;
2088 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2089 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2091 /* Set optimized view status */
2092 priv->optimized_view = !show_toolbar;
2094 if (!parent_priv->toolbar) {
2095 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2097 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2099 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2100 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2101 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2104 hildon_window_add_toolbar (HILDON_WINDOW (self),
2105 GTK_TOOLBAR (parent_priv->toolbar));
2107 /* Set reply button tap and hold menu */
2108 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2109 "/ToolBar/ToolbarMessageReply");
2110 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2111 "/ToolbarReplyCSM");
2112 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2116 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2117 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2118 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2120 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2121 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2122 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2124 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2127 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2128 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2133 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2135 ModestMsgViewWindow *window)
2137 if (!GTK_WIDGET_VISIBLE (window))
2140 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2144 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2146 ModestMsgViewWindowPrivate *priv;
2148 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2149 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2151 return priv->progress_hint;
2155 observers_empty (ModestMsgViewWindow *self)
2158 ModestMsgViewWindowPrivate *priv;
2159 gboolean is_empty = TRUE;
2160 guint pending_ops = 0;
2162 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2163 tmp = priv->progress_widgets;
2165 /* Check all observers */
2166 while (tmp && is_empty) {
2167 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2168 is_empty = pending_ops == 0;
2170 tmp = g_slist_next(tmp);
2177 on_account_removed (TnyAccountStore *account_store,
2178 TnyAccount *account,
2181 /* Do nothing if it's a transport account, because we only
2182 show the messages of a store account */
2183 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2184 const gchar *parent_acc = NULL;
2185 const gchar *our_acc = NULL;
2187 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2188 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2190 /* Close this window if I'm showing a message of the removed account */
2191 if (strcmp (parent_acc, our_acc) == 0)
2192 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2197 on_mail_operation_started (ModestMailOperation *mail_op,
2200 ModestMsgViewWindow *self;
2201 ModestMailOperationTypeOperation op_type;
2203 ModestMsgViewWindowPrivate *priv;
2204 GObject *source = NULL;
2206 self = MODEST_MSG_VIEW_WINDOW (user_data);
2207 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2208 op_type = modest_mail_operation_get_type_operation (mail_op);
2209 tmp = priv->progress_widgets;
2210 source = modest_mail_operation_get_source(mail_op);
2211 if (G_OBJECT (self) == source) {
2212 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2213 set_toolbar_transfer_mode(self);
2215 modest_progress_object_add_operation (
2216 MODEST_PROGRESS_OBJECT (tmp->data),
2218 tmp = g_slist_next (tmp);
2222 g_object_unref (source);
2226 on_mail_operation_finished (ModestMailOperation *mail_op,
2229 ModestMsgViewWindow *self;
2230 ModestMailOperationTypeOperation op_type;
2232 ModestMsgViewWindowPrivate *priv;
2234 self = MODEST_MSG_VIEW_WINDOW (user_data);
2235 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2236 op_type = modest_mail_operation_get_type_operation (mail_op);
2237 tmp = priv->progress_widgets;
2239 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2241 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2243 tmp = g_slist_next (tmp);
2246 /* If no more operations are being observed, NORMAL mode is enabled again */
2247 if (observers_empty (self)) {
2248 set_progress_hint (self, FALSE);
2251 /* Update dimming rules. We have to do this right here
2252 and not in view_msg_cb because at that point the
2253 transfer mode is still enabled so the dimming rule
2254 won't let the user delete the message that has been
2255 readed for example */
2256 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2257 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2262 on_queue_changed (ModestMailOperationQueue *queue,
2263 ModestMailOperation *mail_op,
2264 ModestMailOperationQueueNotification type,
2265 ModestMsgViewWindow *self)
2267 ModestMsgViewWindowPrivate *priv;
2269 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2271 /* If this operations was created by another window, do nothing */
2272 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2275 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2276 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2278 "operation-started",
2279 G_CALLBACK (on_mail_operation_started),
2281 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2283 "operation-finished",
2284 G_CALLBACK (on_mail_operation_finished),
2286 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2287 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2289 "operation-started");
2290 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2292 "operation-finished");
2297 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2299 ModestMsgViewWindowPrivate *priv;
2300 TnyList *selected_attachments = NULL;
2302 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2303 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2305 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2307 return selected_attachments;
2313 guint banner_idle_id;
2314 } DecodeAsyncHelper;
2317 decode_async_banner_idle (gpointer user_data)
2319 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2321 helper->banner_idle_id = 0;
2322 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2328 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2334 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2336 if (helper->banner_idle_id > 0) {
2337 g_source_remove (helper->banner_idle_id);
2338 helper->banner_idle_id = 0;
2340 if (helper->banner) {
2341 gtk_widget_destroy (helper->banner);
2342 helper->banner = NULL;
2344 if (cancelled || err) {
2345 modest_platform_information_banner (NULL, NULL,
2346 _("mail_ib_file_operation_failed"));
2350 /* make the file read-only */
2351 g_chmod(helper->filepath, 0444);
2353 /* Activate the file */
2354 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2358 g_free (helper->filepath);
2359 g_slice_free (DecodeAsyncHelper, helper);
2363 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2364 TnyMimePart *mime_part)
2366 ModestMsgViewWindowPrivate *priv;
2367 const gchar *msg_uid;
2368 gchar *attachment_uid = NULL;
2369 gint attachment_index = 0;
2370 TnyList *attachments;
2372 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2373 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2374 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2376 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2377 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2378 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2379 g_object_unref (attachments);
2381 if (msg_uid && attachment_index >= 0) {
2382 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2385 if (mime_part == NULL) {
2386 gboolean error = FALSE;
2387 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2388 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2390 } else if (tny_list_get_length (selected_attachments) > 1) {
2391 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2395 iter = tny_list_create_iterator (selected_attachments);
2396 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2397 g_object_unref (iter);
2399 g_object_unref (selected_attachments);
2404 g_object_ref (mime_part);
2407 if (tny_mime_part_is_purged (mime_part)) {
2408 g_object_unref (mime_part);
2412 if (!modest_tny_mime_part_is_msg (mime_part)) {
2413 gchar *filepath = NULL;
2414 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2415 gboolean show_error_banner = FALSE;
2416 TnyFsStream *temp_stream = NULL;
2417 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2420 if (temp_stream != NULL) {
2421 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2422 helper->filepath = g_strdup (filepath);
2423 helper->banner = NULL;
2424 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2425 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2426 on_decode_to_stream_async_handler,
2429 g_object_unref (temp_stream);
2430 /* NOTE: files in the temporary area will be automatically
2431 * cleaned after some time if they are no longer in use */
2434 const gchar *content_type;
2435 /* the file may already exist but it isn't writable,
2436 * let's try to open it anyway */
2437 content_type = tny_mime_part_get_content_type (mime_part);
2438 modest_platform_activate_file (filepath, content_type);
2440 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2441 show_error_banner = TRUE;
2446 if (show_error_banner)
2447 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2449 /* message attachment */
2450 TnyHeader *header = NULL;
2451 ModestWindowMgr *mgr;
2452 ModestWindow *msg_win = NULL;
2455 header = tny_msg_get_header (TNY_MSG (mime_part));
2456 mgr = modest_runtime_get_window_mgr ();
2457 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2460 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2461 * thus, we don't do anything */
2462 g_warning ("window for is already being created");
2464 /* it's not found, so create a new window for it */
2465 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2466 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2468 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2469 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2470 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2471 modest_window_get_zoom (MODEST_WINDOW (window)));
2472 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2473 gtk_widget_show_all (GTK_WIDGET (msg_win));
2476 g_object_unref (mime_part);
2489 GnomeVFSResult result;
2492 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2493 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2494 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2495 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2498 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2502 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2503 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2504 g_free (pair->filename);
2505 g_object_unref (pair->part);
2506 g_slice_free (SaveMimePartPair, pair);
2508 g_list_free (info->pairs);
2511 gtk_widget_destroy (info->banner);
2512 g_slice_free (SaveMimePartInfo, info);
2517 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2519 if (info->pairs != NULL) {
2520 save_mime_part_to_file (info);
2522 /* This is a GDK lock because we are an idle callback and
2523 * hildon_banner_show_information is or does Gtk+ code */
2525 gdk_threads_enter (); /* CHECKED */
2526 save_mime_part_info_free (info, TRUE);
2527 if (info->result == GNOME_VFS_OK) {
2528 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2529 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2530 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2531 "cerm_device_memory_full"));
2533 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2535 gdk_threads_leave (); /* CHECKED */
2542 save_mime_part_to_file (SaveMimePartInfo *info)
2544 GnomeVFSHandle *handle;
2546 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2548 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2549 if (info->result == GNOME_VFS_OK) {
2550 GError *error = NULL;
2551 stream = tny_vfs_stream_new (handle);
2552 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2553 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2555 info->result = GNOME_VFS_ERROR_IO;
2557 g_object_unref (G_OBJECT (stream));
2558 g_object_unref (pair->part);
2559 g_slice_free (SaveMimePartPair, pair);
2560 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2562 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2563 save_mime_part_info_free (info, FALSE);
2566 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2571 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2573 gboolean is_ok = TRUE;
2574 gint replaced_files = 0;
2575 const GList *files = info->pairs;
2578 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2579 SaveMimePartPair *pair = iter->data;
2580 if (modest_utils_file_exists (pair->filename)) {
2584 if (replaced_files) {
2585 GtkWidget *confirm_overwrite_dialog;
2586 const gchar *message = (replaced_files == 1) ?
2587 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2588 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2589 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2592 gtk_widget_destroy (confirm_overwrite_dialog);
2596 save_mime_part_info_free (info, TRUE);
2598 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2599 _CS("sfil_ib_saving"));
2600 info->banner = banner;
2601 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2607 save_attachments_response (GtkDialog *dialog,
2611 TnyList *mime_parts;
2613 GList *files_to_save = NULL;
2615 mime_parts = TNY_LIST (user_data);
2617 if (arg1 != GTK_RESPONSE_OK)
2620 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2622 if (!modest_utils_folder_writable (chooser_uri)) {
2623 hildon_banner_show_information
2624 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2628 iter = tny_list_create_iterator (mime_parts);
2629 while (!tny_iterator_is_done (iter)) {
2630 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2632 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2633 !tny_mime_part_is_purged (mime_part) &&
2634 (tny_mime_part_get_filename (mime_part) != NULL)) {
2635 SaveMimePartPair *pair;
2637 pair = g_slice_new0 (SaveMimePartPair);
2639 if (tny_list_get_length (mime_parts) > 1) {
2641 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2642 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2645 pair->filename = g_strdup (chooser_uri);
2647 pair->part = mime_part;
2648 files_to_save = g_list_prepend (files_to_save, pair);
2650 tny_iterator_next (iter);
2652 g_object_unref (iter);
2654 g_free (chooser_uri);
2656 if (files_to_save != NULL) {
2657 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2658 info->pairs = files_to_save;
2659 info->result = TRUE;
2660 save_mime_parts_to_file_with_checks (info);
2664 /* Free and close the dialog */
2665 g_object_unref (mime_parts);
2666 gtk_widget_destroy (GTK_WIDGET (dialog));
2670 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2672 ModestMsgViewWindowPrivate *priv;
2673 GtkWidget *save_dialog = NULL;
2674 gchar *folder = NULL;
2675 gchar *filename = NULL;
2676 gchar *save_multiple_str = NULL;
2678 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2679 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2681 if (mime_parts == NULL) {
2682 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2683 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2686 g_object_ref (mime_parts);
2689 /* prepare dialog */
2690 if (tny_list_get_length (mime_parts) == 1) {
2692 /* only one attachment selected */
2693 iter = tny_list_create_iterator (mime_parts);
2694 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2695 g_object_unref (iter);
2696 if (!modest_tny_mime_part_is_msg (mime_part) &&
2697 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2698 !tny_mime_part_is_purged (mime_part)) {
2699 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2701 /* TODO: show any error? */
2702 g_warning ("Tried to save a non-file attachment");
2703 g_object_unref (mime_parts);
2706 g_object_unref (mime_part);
2708 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2709 tny_list_get_length (mime_parts));
2712 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2713 GTK_FILE_CHOOSER_ACTION_SAVE);
2716 folder = g_build_filename (g_get_home_dir (), g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2717 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2722 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2727 /* if multiple, set multiple string */
2728 if (save_multiple_str) {
2729 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2730 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2733 /* We must run this asynchronously, because the hildon dialog
2734 performs a gtk_dialog_run by itself which leads to gdk
2736 g_signal_connect (save_dialog, "response",
2737 G_CALLBACK (save_attachments_response), mime_parts);
2739 gtk_widget_show_all (save_dialog);
2743 show_remove_attachment_information (gpointer userdata)
2745 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2746 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2748 /* We're outside the main lock */
2749 gdk_threads_enter ();
2751 if (priv->remove_attachment_banner != NULL) {
2752 gtk_widget_destroy (priv->remove_attachment_banner);
2753 g_object_unref (priv->remove_attachment_banner);
2756 priv->remove_attachment_banner = g_object_ref (
2757 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2759 gdk_threads_leave ();
2765 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2767 ModestMsgViewWindowPrivate *priv;
2768 TnyList *mime_parts = NULL;
2769 gchar *confirmation_message;
2775 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2776 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2779 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2781 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2783 /* Remove already purged messages from mime parts list */
2784 iter = tny_list_create_iterator (mime_parts);
2785 while (!tny_iterator_is_done (iter)) {
2786 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2787 tny_iterator_next (iter);
2788 if (tny_mime_part_is_purged (part)) {
2789 tny_list_remove (mime_parts, (GObject *) part);
2791 g_object_unref (part);
2793 g_object_unref (iter);
2795 if (tny_list_get_length (mime_parts) == 0) {
2796 g_object_unref (mime_parts);
2800 n_attachments = tny_list_get_length (mime_parts);
2801 if (n_attachments == 1) {
2805 iter = tny_list_create_iterator (mime_parts);
2806 part = (TnyMimePart *) tny_iterator_get_current (iter);
2807 g_object_unref (iter);
2808 if (modest_tny_mime_part_is_msg (part)) {
2810 header = tny_msg_get_header (TNY_MSG (part));
2811 filename = tny_header_dup_subject (header);
2812 g_object_unref (header);
2813 if (filename == NULL)
2814 filename = g_strdup (_("mail_va_no_subject"));
2816 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2818 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2820 g_object_unref (part);
2822 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2823 "mcen_nc_purge_files_text",
2824 n_attachments), n_attachments);
2826 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2827 confirmation_message);
2828 g_free (confirmation_message);
2830 if (response != GTK_RESPONSE_OK) {
2831 g_object_unref (mime_parts);
2835 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2837 iter = tny_list_create_iterator (mime_parts);
2838 while (!tny_iterator_is_done (iter)) {
2841 part = (TnyMimePart *) tny_iterator_get_current (iter);
2842 tny_mime_part_set_purged (TNY_MIME_PART (part));
2843 g_object_unref (part);
2844 tny_iterator_next (iter);
2846 g_object_unref (iter);
2848 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2849 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2850 tny_msg_rewrite_cache (msg);
2851 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2852 g_object_unref (msg);
2854 g_object_unref (mime_parts);
2856 if (priv->purge_timeout > 0) {
2857 g_source_remove (priv->purge_timeout);
2858 priv->purge_timeout = 0;
2861 if (priv->remove_attachment_banner) {
2862 gtk_widget_destroy (priv->remove_attachment_banner);
2863 g_object_unref (priv->remove_attachment_banner);
2864 priv->remove_attachment_banner = NULL;
2872 update_window_title (ModestMsgViewWindow *window)
2874 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2876 TnyHeader *header = NULL;
2877 gchar *subject = NULL;
2879 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2882 header = tny_msg_get_header (msg);
2883 subject = tny_header_dup_subject (header);
2884 g_object_unref (header);
2885 g_object_unref (msg);
2888 if ((subject == NULL)||(subject[0] == '\0')) {
2890 subject = g_strdup (_("mail_va_no_subject"));
2893 gtk_window_set_title (GTK_WINDOW (window), subject);
2897 static void on_move_focus (GtkWidget *widget,
2898 GtkDirectionType direction,
2901 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
2905 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
2907 GnomeVFSResult result;
2908 GnomeVFSHandle *handle = NULL;
2909 GnomeVFSFileInfo *info = NULL;
2912 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2913 if (result != GNOME_VFS_OK) {
2918 info = gnome_vfs_file_info_new ();
2919 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
2920 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
2921 /* We put a "safe" default size for going to cache */
2922 *expected_size = (300*1024);
2924 *expected_size = info->size;
2926 gnome_vfs_file_info_unref (info);
2928 stream = tny_vfs_stream_new (handle);
2937 TnyStream *output_stream;
2938 GtkWidget *msg_view;
2942 on_fetch_image_idle_refresh_view (gpointer userdata)
2945 FetchImageData *fidata = (FetchImageData *) userdata;
2946 g_message ("REFRESH VIEW");
2947 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
2948 g_message ("QUEUING DRAW");
2949 gtk_widget_queue_draw (fidata->msg_view);
2951 g_object_unref (fidata->msg_view);
2952 g_slice_free (FetchImageData, fidata);
2957 on_fetch_image_thread (gpointer userdata)
2959 FetchImageData *fidata = (FetchImageData *) userdata;
2960 TnyStreamCache *cache;
2961 TnyStream *cache_stream;
2963 cache = modest_runtime_get_images_cache ();
2964 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
2965 g_free (fidata->cache_id);
2966 g_free (fidata->uri);
2968 if (cache_stream != NULL) {
2969 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
2970 tny_stream_close (cache_stream);
2971 g_object_unref (cache_stream);
2974 tny_stream_close (fidata->output_stream);
2975 g_object_unref (fidata->output_stream);
2978 gdk_threads_enter ();
2979 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
2980 gdk_threads_leave ();
2986 on_fetch_image (ModestMsgView *msgview,
2989 ModestMsgViewWindow *window)
2991 const gchar *current_account;
2992 ModestMsgViewWindowPrivate *priv;
2993 FetchImageData *fidata;
2995 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2997 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
2999 fidata = g_slice_new0 (FetchImageData);
3000 fidata->msg_view = g_object_ref (msgview);
3001 fidata->uri = g_strdup (uri);
3002 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3003 fidata->output_stream = g_object_ref (stream);
3005 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3006 g_object_unref (fidata->output_stream);
3007 g_free (fidata->cache_id);
3008 g_free (fidata->uri);
3009 g_object_unref (fidata->msg_view);
3010 g_slice_free (FetchImageData, fidata);
3011 tny_stream_close (stream);
3019 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3021 ModestMsgViewWindowPrivate *priv;
3022 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3023 GSList *recipients = NULL;
3025 gboolean contacts_to_add = FALSE;
3027 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3028 if (msg == NULL) return;
3029 recipients = modest_tny_msg_get_all_recipients_list (msg);
3031 if (recipients != NULL) {
3032 GtkWidget *picker_dialog;
3033 GtkWidget *selector;
3037 selector = hildon_touch_selector_new_text ();
3038 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3039 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3040 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3041 (const gchar *) node->data);
3042 contacts_to_add = TRUE;
3046 if (contacts_to_add) {
3048 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3049 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3051 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3052 HILDON_TOUCH_SELECTOR (selector));
3054 gtk_dialog_run (GTK_DIALOG (picker_dialog));
3055 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3056 gtk_widget_destroy (picker_dialog);
3059 modest_address_book_add_address (selected);
3064 g_object_unref (selector);
3069 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3070 g_object_unref (msg);