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 <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.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>
68 #include <modest-header-window.h>
70 #define MYDOCS_ENV "MYDOCSDIR"
71 #define DOCS_FOLDER ".documents"
73 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
74 struct _ModestMsgViewWindowPrivate {
77 GtkWidget *main_scroll;
78 GtkWidget *find_toolbar;
81 /* Progress observers */
82 GSList *progress_widgets;
85 GtkWidget *prev_toolitem;
86 GtkWidget *next_toolitem;
87 gboolean progress_hint;
89 /* Optimized view enabled */
90 gboolean optimized_view;
92 /* Whether this was created via the *_new_for_search_result() function. */
93 gboolean is_search_result;
95 /* Whether the message is in outbox */
98 /* A reference to the @model of the header view
99 * to allow selecting previous/next messages,
100 * if the message is currently selected in the header view.
102 const gchar *header_folder_id;
103 GtkTreeModel *header_model;
104 GtkTreeRowReference *row_reference;
105 GtkTreeRowReference *next_row_reference;
107 gulong clipboard_change_handler;
108 gulong queue_change_handler;
109 gulong account_removed_handler;
110 gulong row_changed_handler;
111 gulong row_deleted_handler;
112 gulong row_inserted_handler;
113 gulong rows_reordered_handler;
116 GtkWidget *remove_attachment_banner;
123 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
124 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
125 static void modest_header_view_observer_init(
126 ModestHeaderViewObserverIface *iface_class);
127 static void modest_msg_view_window_finalize (GObject *obj);
128 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
130 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
131 ModestMsgViewWindow *obj);
132 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
133 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
137 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
138 static void modest_msg_view_window_set_zoom (ModestWindow *window,
140 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
141 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
142 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
145 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
147 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
148 gboolean show_toolbar);
150 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
152 ModestMsgViewWindow *window);
154 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
157 ModestMsgViewWindow *window);
159 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
161 ModestMsgViewWindow *window);
163 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
164 GtkTreePath *tree_path,
165 GtkTreeIter *tree_iter,
166 ModestMsgViewWindow *window);
168 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
172 ModestMsgViewWindow *window);
174 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
176 const gchar *tny_folder_id);
178 static void on_queue_changed (ModestMailOperationQueue *queue,
179 ModestMailOperation *mail_op,
180 ModestMailOperationQueueNotification type,
181 ModestMsgViewWindow *self);
183 static void on_account_removed (TnyAccountStore *account_store,
187 static void on_move_focus (GtkWidget *widget,
188 GtkDirectionType direction,
191 static void view_msg_cb (ModestMailOperation *mail_op,
198 static void set_progress_hint (ModestMsgViewWindow *self,
201 static void update_window_title (ModestMsgViewWindow *window);
203 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
204 static void init_window (ModestMsgViewWindow *obj);
206 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
208 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
210 static gboolean on_fetch_image (ModestMsgView *msgview,
213 ModestMsgViewWindow *window);
215 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
216 GtkScrollType scroll_type,
219 static gboolean message_reader (ModestMsgViewWindow *window,
220 ModestMsgViewWindowPrivate *priv,
222 GtkTreeRowReference *row_reference);
224 static void setup_menu (ModestMsgViewWindow *self);
225 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
229 /* list my signals */
236 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
237 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
241 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
242 MODEST_TYPE_MSG_VIEW_WINDOW, \
243 ModestMsgViewWindowPrivate))
245 static GtkWindowClass *parent_class = NULL;
247 /* uncomment the following if you have defined any signals */
248 static guint signals[LAST_SIGNAL] = {0};
251 modest_msg_view_window_get_type (void)
253 static GType my_type = 0;
255 static const GTypeInfo my_info = {
256 sizeof(ModestMsgViewWindowClass),
257 NULL, /* base init */
258 NULL, /* base finalize */
259 (GClassInitFunc) modest_msg_view_window_class_init,
260 NULL, /* class finalize */
261 NULL, /* class data */
262 sizeof(ModestMsgViewWindow),
264 (GInstanceInitFunc) modest_msg_view_window_init,
267 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
268 "ModestMsgViewWindow",
271 static const GInterfaceInfo modest_header_view_observer_info =
273 (GInterfaceInitFunc) modest_header_view_observer_init,
274 NULL, /* interface_finalize */
275 NULL /* interface_data */
278 g_type_add_interface_static (my_type,
279 MODEST_TYPE_HEADER_VIEW_OBSERVER,
280 &modest_header_view_observer_info);
286 save_state (ModestWindow *self)
288 modest_widget_memory_save (modest_runtime_get_conf (),
290 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
294 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 HildonWindowClass *hildon_window_class;
329 ModestWindowClass *modest_window_class;
330 GtkBindingSet *binding_set;
332 gobject_class = (GObjectClass*) klass;
333 hildon_window_class = (HildonWindowClass *) klass;
334 modest_window_class = (ModestWindowClass *) klass;
336 parent_class = g_type_class_peek_parent (klass);
337 gobject_class->finalize = modest_msg_view_window_finalize;
339 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
340 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
341 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
342 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
343 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
344 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
346 modest_window_class->save_state_func = save_state;
348 klass->scroll_child = modest_msg_view_window_scroll_child;
350 signals[MSG_CHANGED_SIGNAL] =
351 g_signal_new ("msg-changed",
352 G_TYPE_FROM_CLASS (gobject_class),
354 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
356 modest_marshal_VOID__POINTER_POINTER,
357 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
359 signals[SCROLL_CHILD_SIGNAL] =
360 g_signal_new ("scroll-child",
361 G_TYPE_FROM_CLASS (gobject_class),
362 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
363 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
365 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
366 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
368 binding_set = gtk_binding_set_by_class (klass);
369 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
370 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
371 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
372 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
373 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
374 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
376 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
380 static void modest_header_view_observer_init(
381 ModestHeaderViewObserverIface *iface_class)
383 iface_class->update_func = modest_msg_view_window_update_model_replaced;
387 modest_msg_view_window_init (ModestMsgViewWindow *obj)
389 ModestMsgViewWindowPrivate *priv;
390 ModestWindowPrivate *parent_priv = NULL;
391 GtkActionGroup *action_group = NULL;
392 GError *error = NULL;
393 GdkPixbuf *window_icon;
395 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
396 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
397 parent_priv->ui_manager = gtk_ui_manager_new();
399 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
400 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
402 /* Add common actions */
403 gtk_action_group_add_actions (action_group,
404 modest_action_entries,
405 G_N_ELEMENTS (modest_action_entries),
407 gtk_action_group_add_toggle_actions (action_group,
408 msg_view_toggle_action_entries,
409 G_N_ELEMENTS (msg_view_toggle_action_entries),
412 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
413 g_object_unref (action_group);
415 /* Load the UI definition */
416 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
419 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
420 g_error_free (error);
425 /* Add accelerators */
426 gtk_window_add_accel_group (GTK_WINDOW (obj),
427 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
429 priv->is_search_result = FALSE;
430 priv->is_outbox = FALSE;
432 priv->msg_view = NULL;
433 priv->header_model = NULL;
434 priv->header_folder_id = NULL;
435 priv->clipboard_change_handler = 0;
436 priv->queue_change_handler = 0;
437 priv->account_removed_handler = 0;
438 priv->row_changed_handler = 0;
439 priv->row_deleted_handler = 0;
440 priv->row_inserted_handler = 0;
441 priv->rows_reordered_handler = 0;
442 priv->progress_hint = FALSE;
444 priv->optimized_view = FALSE;
445 priv->purge_timeout = 0;
446 priv->remove_attachment_banner = NULL;
447 priv->msg_uid = NULL;
449 priv->sighandlers = NULL;
452 init_window (MODEST_MSG_VIEW_WINDOW(obj));
454 /* Set window icon */
455 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
457 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
458 g_object_unref (window_icon);
461 hildon_program_add_window (hildon_program_get_instance(),
468 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
470 ModestMsgViewWindowPrivate *priv = NULL;
472 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
474 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
476 set_progress_hint (self, TRUE);
482 set_progress_hint (ModestMsgViewWindow *self,
485 ModestWindowPrivate *parent_priv;
486 ModestMsgViewWindowPrivate *priv;
488 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
490 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
491 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
493 /* Sets current progress hint */
494 priv->progress_hint = enabled;
496 if (GTK_WIDGET_VISIBLE (self)) {
497 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
504 init_window (ModestMsgViewWindow *obj)
506 GtkWidget *main_vbox;
507 ModestMsgViewWindowPrivate *priv;
509 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
511 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
512 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
513 main_vbox = gtk_vbox_new (FALSE, 6);
514 #ifdef MODEST_TOOLKIT_HILDON2
515 priv->main_scroll = hildon_pannable_area_new ();
516 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
518 #ifdef MODEST_USE_MOZEMBED
519 priv->main_scroll = priv->msg_view;
520 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
522 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
523 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
525 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
526 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
527 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
530 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
531 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
533 priv->find_toolbar = hildon_find_toolbar_new (NULL);
534 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
535 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
537 /* NULL-ize fields if the window is destroyed */
538 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
540 gtk_widget_show_all (GTK_WIDGET(main_vbox));
544 modest_msg_view_window_disconnect_signals (ModestWindow *self)
546 ModestMsgViewWindowPrivate *priv;
547 GtkWidget *header_view = NULL;
548 GtkWindow *parent_window = NULL;
550 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
552 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
553 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
554 priv->clipboard_change_handler))
555 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
556 priv->clipboard_change_handler);
558 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
559 priv->queue_change_handler))
560 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
561 priv->queue_change_handler);
563 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
564 priv->account_removed_handler))
565 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
566 priv->account_removed_handler);
568 if (priv->header_model) {
569 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
570 priv->row_changed_handler))
571 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
572 priv->row_changed_handler);
574 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
575 priv->row_deleted_handler))
576 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
577 priv->row_deleted_handler);
579 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
580 priv->row_inserted_handler))
581 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
582 priv->row_inserted_handler);
584 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
585 priv->rows_reordered_handler))
586 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
587 priv->rows_reordered_handler);
590 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
591 priv->sighandlers = NULL;
593 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
594 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
595 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
597 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
598 MODEST_HEADER_VIEW_OBSERVER(self));
604 modest_msg_view_window_finalize (GObject *obj)
606 ModestMsgViewWindowPrivate *priv;
608 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
610 /* Sanity check: shouldn't be needed, the window mgr should
611 call this function before */
612 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
614 if (priv->header_model != NULL) {
615 g_object_unref (priv->header_model);
616 priv->header_model = NULL;
619 if (priv->remove_attachment_banner) {
620 gtk_widget_destroy (priv->remove_attachment_banner);
621 g_object_unref (priv->remove_attachment_banner);
622 priv->remove_attachment_banner = NULL;
625 if (priv->purge_timeout > 0) {
626 g_source_remove (priv->purge_timeout);
627 priv->purge_timeout = 0;
630 if (priv->row_reference) {
631 gtk_tree_row_reference_free (priv->row_reference);
632 priv->row_reference = NULL;
635 if (priv->next_row_reference) {
636 gtk_tree_row_reference_free (priv->next_row_reference);
637 priv->next_row_reference = NULL;
641 g_free (priv->msg_uid);
642 priv->msg_uid = NULL;
645 G_OBJECT_CLASS(parent_class)->finalize (obj);
649 select_next_valid_row (GtkTreeModel *model,
650 GtkTreeRowReference **row_reference,
654 GtkTreeIter tmp_iter;
656 GtkTreePath *next = NULL;
657 gboolean retval = FALSE, finished;
659 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
661 path = gtk_tree_row_reference_get_path (*row_reference);
662 gtk_tree_model_get_iter (model, &tmp_iter, path);
663 gtk_tree_row_reference_free (*row_reference);
664 *row_reference = NULL;
668 TnyHeader *header = NULL;
670 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
671 gtk_tree_model_get (model, &tmp_iter,
672 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
676 if (msg_is_visible (header, is_outbox)) {
677 next = gtk_tree_model_get_path (model, &tmp_iter);
678 *row_reference = gtk_tree_row_reference_new (model, next);
679 gtk_tree_path_free (next);
683 g_object_unref (header);
686 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
687 next = gtk_tree_model_get_path (model, &tmp_iter);
689 /* Ensure that we are not selecting the same */
690 if (gtk_tree_path_compare (path, next) != 0) {
691 gtk_tree_model_get (model, &tmp_iter,
692 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
695 if (msg_is_visible (header, is_outbox)) {
696 *row_reference = gtk_tree_row_reference_new (model, next);
700 g_object_unref (header);
704 /* If we ended up in the same message
705 then there is no valid next
709 gtk_tree_path_free (next);
711 /* If there are no more messages and we don't
712 want to start again in the first one then
713 there is no valid next message */
719 gtk_tree_path_free (path);
724 /* TODO: This should be in _init(), with the parameters as properties. */
726 modest_msg_view_window_construct (ModestMsgViewWindow *self,
727 const gchar *modest_account_name,
728 const gchar *msg_uid)
731 ModestMsgViewWindowPrivate *priv = NULL;
732 ModestWindowPrivate *parent_priv = NULL;
733 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
734 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
736 obj = G_OBJECT (self);
737 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
738 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
740 priv->msg_uid = g_strdup (msg_uid);
743 parent_priv->menubar = NULL;
745 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
746 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
749 /* Add common dimming rules */
750 modest_dimming_rules_group_add_rules (toolbar_rules_group,
751 modest_msg_view_toolbar_dimming_entries,
752 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
753 MODEST_WINDOW (self));
754 modest_dimming_rules_group_add_rules (clipboard_rules_group,
755 modest_msg_view_clipboard_dimming_entries,
756 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
757 MODEST_WINDOW (self));
759 /* Insert dimming rules group for this window */
760 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
761 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
762 g_object_unref (toolbar_rules_group);
763 g_object_unref (clipboard_rules_group);
765 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
767 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);
768 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
769 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
770 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
771 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
772 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
773 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
774 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
775 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
776 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
777 G_CALLBACK (modest_ui_actions_on_details), obj);
778 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
779 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
780 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
781 G_CALLBACK (on_fetch_image), obj);
783 g_signal_connect (G_OBJECT (obj), "key-release-event",
784 G_CALLBACK (modest_msg_view_window_key_event),
787 g_signal_connect (G_OBJECT (obj), "key-press-event",
788 G_CALLBACK (modest_msg_view_window_key_event),
791 g_signal_connect (G_OBJECT (obj), "move-focus",
792 G_CALLBACK (on_move_focus), obj);
794 g_signal_connect (G_OBJECT (obj), "map-event",
795 G_CALLBACK (_modest_msg_view_window_map_event),
798 /* Mail Operation Queue */
799 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
801 G_CALLBACK (on_queue_changed),
804 /* Account manager */
805 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
807 G_CALLBACK(on_account_removed),
810 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
812 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
813 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
814 priv->last_search = NULL;
816 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
818 /* Init the clipboard actions dim status */
819 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
821 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
826 /* FIXME: parameter checks */
828 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
829 const gchar *modest_account_name,
830 const gchar *msg_uid,
832 GtkTreeRowReference *row_reference)
834 ModestMsgViewWindow *window = NULL;
835 ModestMsgViewWindowPrivate *priv = NULL;
836 TnyFolder *header_folder = NULL;
837 ModestHeaderView *header_view = NULL;
838 ModestWindow *main_window = NULL;
839 ModestWindowMgr *mgr = NULL;
842 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
845 mgr = modest_runtime_get_window_mgr ();
846 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
847 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
849 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
851 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
853 /* Remember the message list's TreeModel so we can detect changes
854 * and change the list selection when necessary: */
856 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
858 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
859 MODEST_MAIN_WINDOW(main_window),
860 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
863 if (header_view != NULL){
864 header_folder = modest_header_view_get_folder(header_view);
865 /* This could happen if the header folder was
866 unseleted before opening this msg window (for
867 example if the user selects an account in the
868 folder view of the main window */
870 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
871 priv->header_folder_id = tny_folder_get_id(header_folder);
872 g_assert(priv->header_folder_id != NULL);
873 g_object_unref(header_folder);
877 /* Setup row references and connect signals */
878 priv->header_model = g_object_ref (model);
881 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
882 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
883 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
885 priv->row_reference = NULL;
886 priv->next_row_reference = NULL;
889 /* Connect signals */
890 priv->row_changed_handler =
891 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
892 G_CALLBACK(modest_msg_view_window_on_row_changed),
894 priv->row_deleted_handler =
895 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
896 G_CALLBACK(modest_msg_view_window_on_row_deleted),
898 priv->row_inserted_handler =
899 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
900 G_CALLBACK(modest_msg_view_window_on_row_inserted),
902 priv->rows_reordered_handler =
903 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
904 G_CALLBACK(modest_msg_view_window_on_row_reordered),
907 if (header_view != NULL){
908 modest_header_view_add_observer(header_view,
909 MODEST_HEADER_VIEW_OBSERVER(window));
912 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
913 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
915 /* gtk_widget_show_all (GTK_WIDGET (window)); */
916 modest_msg_view_window_update_priority (window);
917 /* Check dimming rules */
918 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
919 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
920 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
922 return MODEST_WINDOW(window);
926 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
927 const gchar *modest_account_name,
928 const gchar *msg_uid,
929 GtkTreeRowReference *row_reference)
931 ModestMsgViewWindow *window = NULL;
932 ModestMsgViewWindowPrivate *priv = NULL;
933 TnyFolder *header_folder = NULL;
934 ModestWindowMgr *mgr = NULL;
938 mgr = modest_runtime_get_window_mgr ();
939 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
940 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
942 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
944 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
946 /* Remember the message list's TreeModel so we can detect changes
947 * and change the list selection when necessary: */
949 if (header_view != NULL){
950 header_folder = modest_header_view_get_folder(header_view);
951 /* This could happen if the header folder was
952 unseleted before opening this msg window (for
953 example if the user selects an account in the
954 folder view of the main window */
956 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
957 priv->header_folder_id = tny_folder_get_id(header_folder);
958 g_assert(priv->header_folder_id != NULL);
959 g_object_unref(header_folder);
963 /* Setup row references and connect signals */
964 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
965 g_object_ref (priv->header_model);
968 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
969 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
970 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
972 priv->row_reference = NULL;
973 priv->next_row_reference = NULL;
976 /* Connect signals */
977 priv->row_changed_handler =
978 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
979 G_CALLBACK(modest_msg_view_window_on_row_changed),
981 priv->row_deleted_handler =
982 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
983 G_CALLBACK(modest_msg_view_window_on_row_deleted),
985 priv->row_inserted_handler =
986 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
987 G_CALLBACK(modest_msg_view_window_on_row_inserted),
989 priv->rows_reordered_handler =
990 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
991 G_CALLBACK(modest_msg_view_window_on_row_reordered),
994 if (header_view != NULL){
995 modest_header_view_add_observer(header_view,
996 MODEST_HEADER_VIEW_OBSERVER(window));
999 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1001 path = gtk_tree_row_reference_get_path (row_reference);
1002 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1004 gtk_tree_model_get (priv->header_model, &iter,
1005 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1007 message_reader (window, priv, header, row_reference);
1009 gtk_tree_path_free (path);
1011 /* Check dimming rules */
1012 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1013 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1014 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1016 return MODEST_WINDOW(window);
1020 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1021 const gchar *modest_account_name,
1022 const gchar *msg_uid)
1024 ModestMsgViewWindow *window = NULL;
1025 ModestMsgViewWindowPrivate *priv = NULL;
1026 ModestWindowMgr *mgr = NULL;
1028 mgr = modest_runtime_get_window_mgr ();
1029 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1030 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1031 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1033 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1035 /* Remember that this is a search result,
1036 * so we can disable some UI appropriately: */
1037 priv->is_search_result = TRUE;
1039 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1041 update_window_title (window);
1042 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1043 modest_msg_view_window_update_priority (window);
1045 /* Check dimming rules */
1046 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1047 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1048 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1050 return MODEST_WINDOW(window);
1054 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1055 const gchar *modest_account_name,
1056 const gchar *msg_uid)
1058 GObject *obj = NULL;
1059 ModestMsgViewWindowPrivate *priv;
1060 ModestWindowMgr *mgr = NULL;
1062 g_return_val_if_fail (msg, NULL);
1063 mgr = modest_runtime_get_window_mgr ();
1064 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1065 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1066 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1067 modest_account_name, msg_uid);
1069 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1070 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1072 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1074 /* Check dimming rules */
1075 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1076 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1077 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1079 return MODEST_WINDOW(obj);
1083 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1086 ModestMsgViewWindow *window)
1088 check_dimming_rules_after_change (window);
1092 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1094 ModestMsgViewWindow *window)
1096 check_dimming_rules_after_change (window);
1098 /* The window could have dissapeared */
1101 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1103 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1104 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1108 /* On insertions we check if the folder still has the message we are
1109 * showing or do not. If do not, we do nothing. Which means we are still
1110 * not attached to any header folder and thus next/prev buttons are
1111 * still dimmed. Once the message that is shown by msg-view is found, the
1112 * new model of header-view will be attached and the references will be set.
1113 * On each further insertions dimming rules will be checked. However
1114 * this requires extra CPU time at least works.
1115 * (An message might be deleted from TnyFolder and thus will not be
1116 * inserted into the model again for example if it is removed by the
1117 * imap server and the header view is refreshed.)
1120 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1121 GtkTreePath *tree_path,
1122 GtkTreeIter *tree_iter,
1123 ModestMsgViewWindow *window)
1125 ModestMsgViewWindowPrivate *priv = NULL;
1126 TnyHeader *header = NULL;
1128 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1129 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1131 g_assert (model == priv->header_model);
1133 /* Check if the newly inserted message is the same we are actually
1134 * showing. IF not, we should remain detached from the header model
1135 * and thus prev and next toolbar buttons should remain dimmed. */
1136 gtk_tree_model_get (model, tree_iter,
1137 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1140 if (TNY_IS_HEADER (header)) {
1143 uid = modest_tny_folder_get_header_unique_id (header);
1144 if (!g_str_equal(priv->msg_uid, uid)) {
1145 check_dimming_rules_after_change (window);
1147 g_object_unref (G_OBJECT(header));
1151 g_object_unref(G_OBJECT(header));
1154 if (priv->row_reference) {
1155 gtk_tree_row_reference_free (priv->row_reference);
1158 /* Setup row_reference for the actual msg. */
1159 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1160 if (priv->row_reference == NULL) {
1161 g_warning("No reference for msg header item.");
1165 /* Now set up next_row_reference. */
1166 if (priv->next_row_reference) {
1167 gtk_tree_row_reference_free (priv->next_row_reference);
1170 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1171 select_next_valid_row (priv->header_model,
1172 &(priv->next_row_reference), FALSE, priv->is_outbox);
1174 /* Connect the remaining callbacks to become able to detect
1175 * changes in header-view. */
1176 priv->row_changed_handler =
1177 g_signal_connect (priv->header_model, "row-changed",
1178 G_CALLBACK (modest_msg_view_window_on_row_changed),
1180 priv->row_deleted_handler =
1181 g_signal_connect (priv->header_model, "row-deleted",
1182 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1184 priv->rows_reordered_handler =
1185 g_signal_connect (priv->header_model, "rows-reordered",
1186 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1189 check_dimming_rules_after_change (window);
1193 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1197 ModestMsgViewWindow *window)
1199 ModestMsgViewWindowPrivate *priv = NULL;
1200 gboolean already_changed = FALSE;
1202 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1204 /* If the current row was reordered select the proper next
1205 valid row. The same if the next row reference changes */
1206 if (priv->row_reference &&
1207 gtk_tree_row_reference_valid (priv->row_reference)) {
1209 path = gtk_tree_row_reference_get_path (priv->row_reference);
1210 if (gtk_tree_path_compare (path, arg1) == 0) {
1211 if (priv->next_row_reference) {
1212 gtk_tree_row_reference_free (priv->next_row_reference);
1214 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1215 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1216 already_changed = TRUE;
1218 gtk_tree_path_free (path);
1220 if (!already_changed &&
1221 priv->next_row_reference &&
1222 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1224 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1225 if (gtk_tree_path_compare (path, arg1) == 0) {
1226 if (priv->next_row_reference) {
1227 gtk_tree_row_reference_free (priv->next_row_reference);
1229 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1230 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1232 gtk_tree_path_free (path);
1234 check_dimming_rules_after_change (window);
1237 /* The modest_msg_view_window_update_model_replaced implements update
1238 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1239 * actually belongs to the header-view is the same as the TnyFolder of
1240 * the message of msg-view or not. If they are different, there is
1241 * nothing to do. If they are the same, then the model has replaced and
1242 * the reference in msg-view shall be replaced from the old model to
1243 * the new model. In this case the view will be detached from it's
1244 * header folder. From this point the next/prev buttons are dimmed.
1247 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1248 GtkTreeModel *model,
1249 const gchar *tny_folder_id)
1251 ModestMsgViewWindowPrivate *priv = NULL;
1252 ModestMsgViewWindow *window = NULL;
1254 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1255 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1257 window = MODEST_MSG_VIEW_WINDOW(observer);
1258 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1260 /* If there is an other folder in the header-view then we do
1261 * not care about it's model (msg list). Else if the
1262 * header-view shows the folder the msg shown by us is in, we
1263 * shall replace our model reference and make some check. */
1264 if(model == NULL || tny_folder_id == NULL ||
1265 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1268 /* Model is changed(replaced), so we should forget the old
1269 * one. Because there might be other references and there
1270 * might be some change on the model even if we unreferenced
1271 * it, we need to disconnect our signals here. */
1272 if (priv->header_model) {
1273 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1274 priv->row_changed_handler))
1275 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1276 priv->row_changed_handler);
1277 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1278 priv->row_deleted_handler))
1279 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1280 priv->row_deleted_handler);
1281 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1282 priv->row_inserted_handler))
1283 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1284 priv->row_inserted_handler);
1285 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1286 priv->rows_reordered_handler))
1287 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1288 priv->rows_reordered_handler);
1291 if (priv->row_reference)
1292 gtk_tree_row_reference_free (priv->row_reference);
1293 if (priv->next_row_reference)
1294 gtk_tree_row_reference_free (priv->next_row_reference);
1295 g_object_unref(priv->header_model);
1298 priv->row_changed_handler = 0;
1299 priv->row_deleted_handler = 0;
1300 priv->row_inserted_handler = 0;
1301 priv->rows_reordered_handler = 0;
1302 priv->next_row_reference = NULL;
1303 priv->row_reference = NULL;
1304 priv->header_model = NULL;
1307 priv->header_model = g_object_ref (model);
1309 /* Also we must connect to the new model for row insertions.
1310 * Only for insertions now. We will need other ones only after
1311 * the msg is show by msg-view is added to the new model. */
1312 priv->row_inserted_handler =
1313 g_signal_connect (priv->header_model, "row-inserted",
1314 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1317 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1318 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1322 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1324 ModestMsgViewWindowPrivate *priv= NULL;
1326 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1327 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1329 return priv->progress_hint;
1333 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1335 ModestMsgViewWindowPrivate *priv= NULL;
1337 TnyHeader *header = NULL;
1338 GtkTreePath *path = NULL;
1341 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1342 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1344 /* If the message was not obtained from a treemodel,
1345 * for instance if it was opened directly by the search UI:
1347 if (priv->header_model == NULL ||
1348 priv->row_reference == NULL ||
1349 !gtk_tree_row_reference_valid (priv->row_reference)) {
1350 msg = modest_msg_view_window_get_message (self);
1352 header = tny_msg_get_header (msg);
1353 g_object_unref (msg);
1358 /* Get iter of the currently selected message in the header view: */
1359 path = gtk_tree_row_reference_get_path (priv->row_reference);
1360 g_return_val_if_fail (path != NULL, NULL);
1361 gtk_tree_model_get_iter (priv->header_model,
1365 /* Get current message header */
1366 gtk_tree_model_get (priv->header_model, &iter,
1367 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1370 gtk_tree_path_free (path);
1375 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1377 ModestMsgViewWindowPrivate *priv;
1379 g_return_val_if_fail (self, NULL);
1381 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1383 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1387 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1389 ModestMsgViewWindowPrivate *priv;
1391 g_return_val_if_fail (self, NULL);
1393 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1395 return (const gchar*) priv->msg_uid;
1399 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1402 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1403 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1404 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1408 is_active = gtk_toggle_action_get_active (toggle);
1411 gtk_widget_show (priv->find_toolbar);
1412 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1414 gtk_widget_hide (priv->find_toolbar);
1415 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1418 /* update the toggle buttons status */
1419 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1421 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1426 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1427 ModestMsgViewWindow *obj)
1429 GtkToggleAction *toggle;
1430 ModestWindowPrivate *parent_priv;
1431 ModestMsgViewWindowPrivate *priv;
1433 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1434 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1436 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1437 gtk_toggle_action_set_active (toggle, FALSE);
1438 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1442 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1443 ModestMsgViewWindow *obj)
1445 gchar *current_search;
1446 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1448 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1449 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1453 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1455 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1456 g_free (current_search);
1457 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1461 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1463 g_free (priv->last_search);
1464 priv->last_search = g_strdup (current_search);
1465 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1468 hildon_banner_show_information (NULL, NULL,
1469 _HL("ckct_ib_find_no_matches"));
1470 g_free (priv->last_search);
1471 priv->last_search = NULL;
1473 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1476 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1477 hildon_banner_show_information (NULL, NULL,
1478 _HL("ckct_ib_find_search_complete"));
1479 g_free (priv->last_search);
1480 priv->last_search = NULL;
1482 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1486 g_free (current_search);
1491 modest_msg_view_window_set_zoom (ModestWindow *window,
1494 ModestMsgViewWindowPrivate *priv;
1495 ModestWindowPrivate *parent_priv;
1497 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1499 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1500 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1501 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1506 modest_msg_view_window_get_zoom (ModestWindow *window)
1508 ModestMsgViewWindowPrivate *priv;
1510 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1512 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1513 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1517 modest_msg_view_window_zoom_plus (ModestWindow *window)
1520 ModestMsgViewWindowPrivate *priv;
1524 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1527 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1529 if (zoom_level >= 2.0) {
1530 hildon_banner_show_information (NULL, NULL,
1531 _CS("ckct_ib_max_zoom_level_reached"));
1533 } else if (zoom_level >= 1.5) {
1535 } else if (zoom_level >= 1.2) {
1537 } else if (zoom_level >= 1.0) {
1539 } else if (zoom_level >= 0.8) {
1541 } else if (zoom_level >= 0.5) {
1547 /* set zoom level */
1548 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1549 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1550 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1551 g_free (banner_text);
1552 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1558 modest_msg_view_window_zoom_minus (ModestWindow *window)
1561 ModestMsgViewWindowPrivate *priv;
1565 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1566 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1568 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1570 if (zoom_level <= 0.5) {
1571 hildon_banner_show_information (NULL, NULL,
1572 _CS("ckct_ib_min_zoom_level_reached"));
1574 } else if (zoom_level <= 0.8) {
1576 } else if (zoom_level <= 1.0) {
1578 } else if (zoom_level <= 1.2) {
1580 } else if (zoom_level <= 1.5) {
1582 } else if (zoom_level <= 2.0) {
1588 /* set zoom level */
1589 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1590 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1591 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1592 g_free (banner_text);
1593 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1600 modest_msg_view_window_key_event (GtkWidget *window,
1606 focus = gtk_window_get_focus (GTK_WINDOW (window));
1608 /* for the find toolbar case */
1609 if (focus && GTK_IS_ENTRY (focus)) {
1610 if (event->keyval == GDK_BackSpace) {
1612 copy = gdk_event_copy ((GdkEvent *) event);
1613 gtk_widget_event (focus, copy);
1614 gdk_event_free (copy);
1619 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1620 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1621 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1622 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1623 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1624 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1625 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1626 /* gboolean return_value; */
1628 if (event->type == GDK_KEY_PRESS) {
1629 GtkScrollType scroll_type;
1631 switch (event->keyval) {
1634 scroll_type = GTK_SCROLL_STEP_UP; break;
1637 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1639 case GDK_KP_Page_Up:
1640 scroll_type = GTK_SCROLL_PAGE_UP; break;
1642 case GDK_KP_Page_Down:
1643 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1646 scroll_type = GTK_SCROLL_START; break;
1649 scroll_type = GTK_SCROLL_END; break;
1650 default: scroll_type = GTK_SCROLL_NONE;
1653 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1654 /* scroll_type, FALSE, &return_value); */
1665 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1668 ModestMsgViewWindowPrivate *priv;
1669 GtkTreeIter tmp_iter;
1670 gboolean is_last_selected;
1672 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1673 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1675 /*if no model (so no rows at all), then virtually we are the last*/
1676 if (!priv->header_model || !priv->row_reference)
1679 if (!gtk_tree_row_reference_valid (priv->row_reference))
1682 path = gtk_tree_row_reference_get_path (priv->row_reference);
1686 is_last_selected = TRUE;
1687 while (is_last_selected) {
1689 gtk_tree_path_next (path);
1690 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1692 gtk_tree_model_get (priv->header_model, &tmp_iter,
1693 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1696 if (msg_is_visible (header, priv->is_outbox))
1697 is_last_selected = FALSE;
1698 g_object_unref(G_OBJECT(header));
1701 gtk_tree_path_free (path);
1702 return is_last_selected;
1706 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1708 ModestMsgViewWindowPrivate *priv;
1710 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1711 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1713 return priv->header_model != NULL;
1717 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1719 ModestMsgViewWindowPrivate *priv;
1721 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1722 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1724 return priv->is_search_result;
1728 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1730 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1732 if (!check_outbox) {
1735 ModestTnySendQueueStatus status;
1736 status = modest_tny_all_send_queues_get_msg_status (header);
1737 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1738 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1743 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1746 ModestMsgViewWindowPrivate *priv;
1747 gboolean is_first_selected;
1748 GtkTreeIter tmp_iter;
1750 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1751 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1753 /*if no model (so no rows at all), then virtually we are the first*/
1754 if (!priv->header_model || !priv->row_reference)
1757 if (!gtk_tree_row_reference_valid (priv->row_reference))
1760 path = gtk_tree_row_reference_get_path (priv->row_reference);
1764 is_first_selected = TRUE;
1765 while (is_first_selected) {
1767 if(!gtk_tree_path_prev (path))
1769 /* Here the 'if' is needless for logic, but let make sure
1770 * iter is valid for gtk_tree_model_get. */
1771 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1773 gtk_tree_model_get (priv->header_model, &tmp_iter,
1774 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1777 if (msg_is_visible (header, priv->is_outbox))
1778 is_first_selected = FALSE;
1779 g_object_unref(G_OBJECT(header));
1782 gtk_tree_path_free (path);
1783 return is_first_selected;
1788 GtkTreeRowReference *row_reference;
1792 message_reader_performer (gboolean canceled,
1794 GtkWindow *parent_window,
1795 TnyAccount *account,
1798 ModestMailOperation *mail_op = NULL;
1799 MsgReaderInfo *info;
1801 info = (MsgReaderInfo *) user_data;
1802 if (canceled || err) {
1803 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1807 /* Register the header - it'll be unregistered in the callback */
1808 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1810 /* New mail operation */
1811 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1812 modest_ui_actions_disk_operations_error_handler,
1815 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1816 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1817 g_object_unref (mail_op);
1819 /* Update dimming rules */
1820 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1821 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1824 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1825 g_object_unref (info->header);
1826 g_slice_free (MsgReaderInfo, info);
1831 * Reads the message whose summary item is @header. It takes care of
1832 * several things, among others:
1834 * If the message was not previously downloaded then ask the user
1835 * before downloading. If there is no connection launch the connection
1836 * dialog. Update toolbar dimming rules.
1838 * Returns: TRUE if the mail operation was started, otherwise if the
1839 * user do not want to download the message, or if the user do not
1840 * want to connect, then the operation is not issued
1843 message_reader (ModestMsgViewWindow *window,
1844 ModestMsgViewWindowPrivate *priv,
1846 GtkTreeRowReference *row_reference)
1848 ModestWindowMgr *mgr;
1849 TnyAccount *account;
1851 MsgReaderInfo *info;
1853 g_return_val_if_fail (row_reference != NULL, FALSE);
1855 mgr = modest_runtime_get_window_mgr ();
1856 /* Msg download completed */
1857 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1859 /* We set the header from model while we're loading */
1860 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1861 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1863 /* Ask the user if he wants to download the message if
1865 if (!tny_device_is_online (modest_runtime_get_device())) {
1866 GtkResponseType response;
1868 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1869 _("mcen_nc_get_msg"));
1870 if (response == GTK_RESPONSE_CANCEL) {
1871 update_window_title (window);
1875 folder = tny_header_get_folder (header);
1876 info = g_slice_new (MsgReaderInfo);
1877 info->header = g_object_ref (header);
1878 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1880 /* Offer the connection dialog if necessary */
1881 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1883 TNY_FOLDER_STORE (folder),
1884 message_reader_performer,
1886 g_object_unref (folder);
1891 folder = tny_header_get_folder (header);
1892 account = tny_folder_get_account (folder);
1893 info = g_slice_new (MsgReaderInfo);
1894 info->header = g_object_ref (header);
1895 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1897 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1898 g_object_unref (account);
1899 g_object_unref (folder);
1905 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1907 ModestMsgViewWindowPrivate *priv;
1908 GtkTreePath *path= NULL;
1909 GtkTreeIter tmp_iter;
1911 gboolean retval = TRUE;
1912 GtkTreeRowReference *row_reference = NULL;
1914 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1915 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1917 if (!priv->row_reference)
1920 /* Update the next row reference if it's not valid. This could
1921 happen if for example the header which it was pointing to,
1922 was deleted. The best place to do it is in the row-deleted
1923 handler but the tinymail model do not work like the glib
1924 tree models and reports the deletion when the row is still
1926 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1927 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1928 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1929 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1932 if (priv->next_row_reference)
1933 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1937 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1939 gtk_tree_model_get_iter (priv->header_model,
1942 gtk_tree_path_free (path);
1944 gtk_tree_model_get (priv->header_model, &tmp_iter,
1945 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1948 /* Read the message & show it */
1949 if (!message_reader (window, priv, header, row_reference)) {
1952 gtk_tree_row_reference_free (row_reference);
1955 g_object_unref (header);
1961 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1963 ModestMsgViewWindowPrivate *priv = NULL;
1965 gboolean finished = FALSE;
1966 gboolean retval = FALSE;
1968 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1969 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1971 /* Return inmediatly if there is no header model */
1972 if (!priv->header_model || !priv->row_reference)
1975 path = gtk_tree_row_reference_get_path (priv->row_reference);
1976 while (!finished && gtk_tree_path_prev (path)) {
1980 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1981 gtk_tree_model_get (priv->header_model, &iter,
1982 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1986 if (msg_is_visible (header, priv->is_outbox)) {
1987 GtkTreeRowReference *row_reference;
1988 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1989 /* Read the message & show it */
1990 retval = message_reader (window, priv, header, row_reference);
1991 gtk_tree_row_reference_free (row_reference);
1995 g_object_unref (header);
1999 gtk_tree_path_free (path);
2004 view_msg_cb (ModestMailOperation *mail_op,
2011 ModestMsgViewWindow *self = NULL;
2012 ModestMsgViewWindowPrivate *priv = NULL;
2013 GtkTreeRowReference *row_reference = NULL;
2015 /* Unregister the header (it was registered before creating the mail operation) */
2016 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2018 row_reference = (GtkTreeRowReference *) user_data;
2020 gtk_tree_row_reference_free (row_reference);
2021 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2023 /* Restore window title */
2024 update_window_title (self);
2025 g_object_unref (self);
2030 /* If there was any error */
2031 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2032 gtk_tree_row_reference_free (row_reference);
2033 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2035 /* Restore window title */
2036 update_window_title (self);
2037 g_object_unref (self);
2042 /* Get the window */
2043 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2044 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2045 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2047 /* Update the row reference */
2048 if (priv->row_reference != NULL) {
2049 gtk_tree_row_reference_free (priv->row_reference);
2050 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2051 if (priv->next_row_reference != NULL) {
2052 gtk_tree_row_reference_free (priv->next_row_reference);
2054 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2055 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2058 /* Mark header as read */
2059 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2060 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2062 /* Set new message */
2063 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2064 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2065 modest_msg_view_window_update_priority (self);
2066 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2067 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2070 /* Set the new message uid of the window */
2071 if (priv->msg_uid) {
2072 g_free (priv->msg_uid);
2073 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2076 /* Notify the observers */
2077 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2078 0, priv->header_model, priv->row_reference);
2081 g_object_unref (self);
2082 gtk_tree_row_reference_free (row_reference);
2086 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2088 ModestMsgViewWindowPrivate *priv;
2090 TnyFolderType folder_type;
2092 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2094 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2096 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2100 folder = tny_msg_get_folder (msg);
2102 folder_type = modest_tny_folder_guess_folder_type (folder);
2103 g_object_unref (folder);
2105 g_object_unref (msg);
2113 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2115 ModestMsgViewWindowPrivate *priv;
2116 TnyHeader *header = NULL;
2117 TnyHeaderFlags flags = 0;
2119 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2121 if (priv->header_model && priv->row_reference) {
2123 GtkTreePath *path = NULL;
2125 path = gtk_tree_row_reference_get_path (priv->row_reference);
2126 g_return_if_fail (path != NULL);
2127 gtk_tree_model_get_iter (priv->header_model,
2129 gtk_tree_row_reference_get_path (priv->row_reference));
2131 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2133 gtk_tree_path_free (path);
2136 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2138 header = tny_msg_get_header (msg);
2139 g_object_unref (msg);
2144 flags = tny_header_get_flags (header);
2145 g_object_unref(G_OBJECT(header));
2148 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2153 toolbar_resize (ModestMsgViewWindow *self)
2155 ModestMsgViewWindowPrivate *priv = NULL;
2156 ModestWindowPrivate *parent_priv = NULL;
2158 gint static_button_size;
2159 ModestWindowMgr *mgr;
2161 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2162 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2163 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2165 mgr = modest_runtime_get_window_mgr ();
2166 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2168 if (parent_priv->toolbar) {
2169 /* left size buttons */
2170 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2171 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2172 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2173 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2174 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2175 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2176 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2177 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2178 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2179 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2180 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2181 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2182 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2183 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2184 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2185 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2187 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2188 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2189 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2190 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2195 modest_msg_view_window_show_toolbar (ModestWindow *self,
2196 gboolean show_toolbar)
2198 ModestMsgViewWindowPrivate *priv = NULL;
2199 ModestWindowPrivate *parent_priv;
2201 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2202 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2204 /* Set optimized view status */
2205 priv->optimized_view = !show_toolbar;
2207 if (!parent_priv->toolbar) {
2208 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2210 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2211 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2213 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2214 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2215 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2218 hildon_window_add_toolbar (HILDON_WINDOW (self),
2219 GTK_TOOLBAR (parent_priv->toolbar));
2224 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2225 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2226 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2228 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2229 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2230 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2232 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2235 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2236 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2241 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2243 ModestMsgViewWindow *window)
2245 if (!GTK_WIDGET_VISIBLE (window))
2248 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2252 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2254 ModestMsgViewWindowPrivate *priv;
2256 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2257 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2259 return priv->progress_hint;
2263 observers_empty (ModestMsgViewWindow *self)
2266 ModestMsgViewWindowPrivate *priv;
2267 gboolean is_empty = TRUE;
2268 guint pending_ops = 0;
2270 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2271 tmp = priv->progress_widgets;
2273 /* Check all observers */
2274 while (tmp && is_empty) {
2275 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2276 is_empty = pending_ops == 0;
2278 tmp = g_slist_next(tmp);
2285 on_account_removed (TnyAccountStore *account_store,
2286 TnyAccount *account,
2289 /* Do nothing if it's a transport account, because we only
2290 show the messages of a store account */
2291 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2292 const gchar *parent_acc = NULL;
2293 const gchar *our_acc = NULL;
2295 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2296 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2298 /* Close this window if I'm showing a message of the removed account */
2299 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2300 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2305 on_mail_operation_started (ModestMailOperation *mail_op,
2308 ModestMsgViewWindow *self;
2309 ModestMailOperationTypeOperation op_type;
2311 ModestMsgViewWindowPrivate *priv;
2312 GObject *source = NULL;
2314 self = MODEST_MSG_VIEW_WINDOW (user_data);
2315 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2316 op_type = modest_mail_operation_get_type_operation (mail_op);
2317 tmp = priv->progress_widgets;
2318 source = modest_mail_operation_get_source(mail_op);
2319 if (G_OBJECT (self) == source) {
2320 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2321 set_toolbar_transfer_mode(self);
2323 modest_progress_object_add_operation (
2324 MODEST_PROGRESS_OBJECT (tmp->data),
2326 tmp = g_slist_next (tmp);
2330 g_object_unref (source);
2334 on_mail_operation_finished (ModestMailOperation *mail_op,
2337 ModestMsgViewWindow *self;
2338 ModestMailOperationTypeOperation op_type;
2340 ModestMsgViewWindowPrivate *priv;
2342 self = MODEST_MSG_VIEW_WINDOW (user_data);
2343 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2344 op_type = modest_mail_operation_get_type_operation (mail_op);
2345 tmp = priv->progress_widgets;
2347 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2349 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2351 tmp = g_slist_next (tmp);
2354 /* If no more operations are being observed, NORMAL mode is enabled again */
2355 if (observers_empty (self)) {
2356 set_progress_hint (self, FALSE);
2360 /* Update dimming rules. We have to do this right here
2361 and not in view_msg_cb because at that point the
2362 transfer mode is still enabled so the dimming rule
2363 won't let the user delete the message that has been
2364 readed for example */
2365 check_dimming_rules_after_change (self);
2370 on_queue_changed (ModestMailOperationQueue *queue,
2371 ModestMailOperation *mail_op,
2372 ModestMailOperationQueueNotification type,
2373 ModestMsgViewWindow *self)
2375 ModestMsgViewWindowPrivate *priv;
2377 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2379 /* If this operations was created by another window, do nothing */
2380 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2383 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2384 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2386 "operation-started",
2387 G_CALLBACK (on_mail_operation_started),
2389 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2391 "operation-finished",
2392 G_CALLBACK (on_mail_operation_finished),
2394 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2395 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2397 "operation-started");
2398 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2400 "operation-finished");
2405 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2407 ModestMsgViewWindowPrivate *priv;
2408 TnyList *selected_attachments = NULL;
2410 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2411 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2413 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2414 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2416 return selected_attachments;
2420 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2426 gchar *filepath = (gchar *) user_data;
2428 if (cancelled || err) {
2430 modest_platform_information_banner (NULL, NULL,
2431 _KR("cerm_device_memory_full"));
2436 /* make the file read-only */
2437 g_chmod(filepath, 0444);
2439 /* Activate the file */
2440 modest_platform_activate_file (filepath, tny_mime_part_get_content_type (mime_part));
2448 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2449 TnyMimePart *mime_part)
2451 ModestMsgViewWindowPrivate *priv;
2452 const gchar *msg_uid;
2453 gchar *attachment_uid = NULL;
2454 gint attachment_index = 0;
2455 TnyList *attachments;
2457 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2458 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2459 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2461 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2462 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2463 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2464 g_object_unref (attachments);
2466 if (msg_uid && attachment_index >= 0) {
2467 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2470 if (mime_part == NULL) {
2471 gboolean error = FALSE;
2472 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2473 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2475 } else if (tny_list_get_length (selected_attachments) > 1) {
2476 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2480 iter = tny_list_create_iterator (selected_attachments);
2481 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2482 g_object_unref (iter);
2484 if (selected_attachments)
2485 g_object_unref (selected_attachments);
2490 g_object_ref (mime_part);
2493 if (tny_mime_part_is_purged (mime_part))
2496 if (!modest_tny_mime_part_is_msg (mime_part)) {
2497 gchar *filepath = NULL;
2498 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2499 gboolean show_error_banner = FALSE;
2500 TnyFsStream *temp_stream = NULL;
2501 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2504 if (temp_stream != NULL) {
2505 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2506 on_decode_to_stream_async_handler,
2508 g_strdup (filepath));
2509 g_object_unref (temp_stream);
2510 /* NOTE: files in the temporary area will be automatically
2511 * cleaned after some time if they are no longer in use */
2514 const gchar *content_type;
2515 /* the file may already exist but it isn't writable,
2516 * let's try to open it anyway */
2517 content_type = tny_mime_part_get_content_type (mime_part);
2518 modest_platform_activate_file (filepath, content_type);
2520 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2521 show_error_banner = TRUE;
2526 if (show_error_banner)
2527 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2529 /* message attachment */
2530 TnyHeader *header = NULL;
2531 ModestWindowMgr *mgr;
2532 ModestWindow *msg_win = NULL;
2535 header = tny_msg_get_header (TNY_MSG (mime_part));
2536 mgr = modest_runtime_get_window_mgr ();
2537 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2540 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2541 * thus, we don't do anything */
2542 g_warning ("window for is already being created");
2544 /* it's not found, so create a new window for it */
2545 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2546 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2548 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2549 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2550 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2551 modest_window_get_zoom (MODEST_WINDOW (window)));
2552 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2553 gtk_widget_show_all (GTK_WIDGET (msg_win));
2559 g_free (attachment_uid);
2561 g_object_unref (mime_part);
2573 GnomeVFSResult result;
2576 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2577 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2578 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2579 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2582 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2586 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2587 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2588 g_free (pair->filename);
2589 g_object_unref (pair->part);
2590 g_slice_free (SaveMimePartPair, pair);
2592 g_list_free (info->pairs);
2595 g_slice_free (SaveMimePartInfo, info);
2600 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2602 if (info->pairs != NULL) {
2603 save_mime_part_to_file (info);
2605 /* This is a GDK lock because we are an idle callback and
2606 * hildon_banner_show_information is or does Gtk+ code */
2608 gdk_threads_enter (); /* CHECKED */
2609 save_mime_part_info_free (info, TRUE);
2610 if (info->result == GNOME_VFS_OK) {
2611 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2612 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2613 hildon_banner_show_information (NULL, NULL,
2614 _KR("cerm_device_memory_full"));
2616 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2618 gdk_threads_leave (); /* CHECKED */
2625 save_mime_part_to_file (SaveMimePartInfo *info)
2627 GnomeVFSHandle *handle;
2629 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2631 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2632 if (info->result == GNOME_VFS_OK) {
2633 GError *error = NULL;
2634 stream = tny_vfs_stream_new (handle);
2635 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2636 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2638 if ((error->domain == TNY_ERROR_DOMAIN) &&
2639 (error->code == TNY_IO_ERROR_WRITE) &&
2640 (errno == ENOSPC)) {
2641 info->result = GNOME_VFS_ERROR_NO_SPACE;
2643 info->result = GNOME_VFS_ERROR_IO;
2646 g_object_unref (G_OBJECT (stream));
2647 g_object_unref (pair->part);
2648 g_slice_free (SaveMimePartPair, pair);
2649 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2651 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2652 save_mime_part_info_free (info, FALSE);
2655 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2660 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2662 gboolean is_ok = TRUE;
2663 gint replaced_files = 0;
2664 const GList *files = info->pairs;
2667 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2668 SaveMimePartPair *pair = iter->data;
2669 if (modest_utils_file_exists (pair->filename)) {
2673 if (replaced_files) {
2674 GtkWidget *confirm_overwrite_dialog;
2675 const gchar *message = (replaced_files == 1) ?
2676 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2677 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2678 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2681 gtk_widget_destroy (confirm_overwrite_dialog);
2685 save_mime_part_info_free (info, TRUE);
2687 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2693 save_attachments_response (GtkDialog *dialog,
2697 TnyList *mime_parts;
2699 GList *files_to_save = NULL;
2701 mime_parts = TNY_LIST (user_data);
2703 if (arg1 != GTK_RESPONSE_OK)
2706 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2708 if (!modest_utils_folder_writable (chooser_uri)) {
2709 hildon_banner_show_information
2710 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2714 iter = tny_list_create_iterator (mime_parts);
2715 while (!tny_iterator_is_done (iter)) {
2716 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2718 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2719 !tny_mime_part_is_purged (mime_part) &&
2720 (tny_mime_part_get_filename (mime_part) != NULL)) {
2721 SaveMimePartPair *pair;
2723 pair = g_slice_new0 (SaveMimePartPair);
2725 if (tny_list_get_length (mime_parts) > 1) {
2727 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2728 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2731 pair->filename = g_strdup (chooser_uri);
2733 pair->part = mime_part;
2734 files_to_save = g_list_prepend (files_to_save, pair);
2736 tny_iterator_next (iter);
2738 g_object_unref (iter);
2740 g_free (chooser_uri);
2742 if (files_to_save != NULL) {
2743 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2744 info->pairs = files_to_save;
2745 info->result = TRUE;
2746 save_mime_parts_to_file_with_checks (info);
2750 /* Free and close the dialog */
2751 g_object_unref (mime_parts);
2752 gtk_widget_destroy (GTK_WIDGET (dialog));
2756 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2758 ModestMsgViewWindowPrivate *priv;
2759 GtkWidget *save_dialog = NULL;
2760 gchar *folder = NULL;
2761 gchar *filename = NULL;
2762 gchar *save_multiple_str = NULL;
2764 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2765 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2767 if (mime_parts == NULL) {
2768 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2769 * selection available */
2770 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2771 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2772 g_object_unref (mime_parts);
2775 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2778 g_object_ref (mime_parts);
2781 /* prepare dialog */
2782 if (tny_list_get_length (mime_parts) == 1) {
2784 /* only one attachment selected */
2785 iter = tny_list_create_iterator (mime_parts);
2786 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2787 g_object_unref (iter);
2788 if (!modest_tny_mime_part_is_msg (mime_part) &&
2789 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2790 !tny_mime_part_is_purged (mime_part)) {
2791 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2793 /* TODO: show any error? */
2794 g_warning ("Tried to save a non-file attachment");
2795 g_object_unref (mime_parts);
2798 g_object_unref (mime_part);
2800 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2801 tny_list_get_length (mime_parts));
2804 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2805 GTK_FILE_CHOOSER_ACTION_SAVE);
2808 folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2809 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2814 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2819 /* if multiple, set multiple string */
2820 if (save_multiple_str) {
2821 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2822 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2825 /* We must run this asynchronously, because the hildon dialog
2826 performs a gtk_dialog_run by itself which leads to gdk
2828 g_signal_connect (save_dialog, "response",
2829 G_CALLBACK (save_attachments_response), mime_parts);
2831 gtk_widget_show_all (save_dialog);
2835 show_remove_attachment_information (gpointer userdata)
2837 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2838 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2840 /* We're outside the main lock */
2841 gdk_threads_enter ();
2843 if (priv->remove_attachment_banner != NULL) {
2844 gtk_widget_destroy (priv->remove_attachment_banner);
2845 g_object_unref (priv->remove_attachment_banner);
2848 priv->remove_attachment_banner = g_object_ref (
2849 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2851 gdk_threads_leave ();
2857 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2859 ModestMsgViewWindowPrivate *priv;
2860 TnyList *mime_parts = NULL;
2861 gchar *confirmation_message;
2867 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2868 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2870 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2871 * because we don't have selection
2873 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2875 /* Remove already purged messages from mime parts list */
2876 iter = tny_list_create_iterator (mime_parts);
2877 while (!tny_iterator_is_done (iter)) {
2878 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2879 tny_iterator_next (iter);
2880 if (tny_mime_part_is_purged (part)) {
2881 tny_list_remove (mime_parts, (GObject *) part);
2883 g_object_unref (part);
2885 g_object_unref (iter);
2887 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
2888 tny_list_get_length (mime_parts) == 0) {
2889 g_object_unref (mime_parts);
2893 n_attachments = tny_list_get_length (mime_parts);
2894 if (n_attachments == 1) {
2898 iter = tny_list_create_iterator (mime_parts);
2899 part = (TnyMimePart *) tny_iterator_get_current (iter);
2900 g_object_unref (iter);
2901 if (modest_tny_mime_part_is_msg (part)) {
2903 header = tny_msg_get_header (TNY_MSG (part));
2904 filename = tny_header_dup_subject (header);
2905 g_object_unref (header);
2906 if (filename == NULL)
2907 filename = g_strdup (_("mail_va_no_subject"));
2909 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2911 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2913 g_object_unref (part);
2915 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2916 "mcen_nc_purge_files_text",
2917 n_attachments), n_attachments);
2919 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2920 confirmation_message);
2921 g_free (confirmation_message);
2923 if (response != GTK_RESPONSE_OK) {
2924 g_object_unref (mime_parts);
2928 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2930 iter = tny_list_create_iterator (mime_parts);
2931 while (!tny_iterator_is_done (iter)) {
2934 part = (TnyMimePart *) tny_iterator_get_current (iter);
2935 tny_mime_part_set_purged (TNY_MIME_PART (part));
2936 g_object_unref (part);
2937 tny_iterator_next (iter);
2939 g_object_unref (iter);
2941 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2942 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2943 tny_msg_rewrite_cache (msg);
2944 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2945 g_object_unref (msg);
2947 g_object_unref (mime_parts);
2949 if (priv->purge_timeout > 0) {
2950 g_source_remove (priv->purge_timeout);
2951 priv->purge_timeout = 0;
2954 if (priv->remove_attachment_banner) {
2955 gtk_widget_destroy (priv->remove_attachment_banner);
2956 g_object_unref (priv->remove_attachment_banner);
2957 priv->remove_attachment_banner = NULL;
2965 update_window_title (ModestMsgViewWindow *window)
2967 ModestMsgViewWindowPrivate *priv;
2969 TnyHeader *header = NULL;
2970 gchar *subject = NULL;
2972 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2974 /* Note that if the window is closed while we're retrieving
2975 the message, this widget could de deleted */
2976 if (!priv->msg_view)
2979 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2982 header = tny_msg_get_header (msg);
2983 subject = tny_header_dup_subject (header);
2984 g_object_unref (header);
2985 g_object_unref (msg);
2988 if ((subject == NULL)||(subject[0] == '\0')) {
2990 subject = g_strdup (_("mail_va_no_subject"));
2993 gtk_window_set_title (GTK_WINDOW (window), subject);
2998 on_move_focus (GtkWidget *widget,
2999 GtkDirectionType direction,
3002 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3006 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3008 GnomeVFSResult result;
3009 GnomeVFSHandle *handle = NULL;
3010 GnomeVFSFileInfo *info = NULL;
3013 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3014 if (result != GNOME_VFS_OK) {
3019 info = gnome_vfs_file_info_new ();
3020 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3021 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3022 /* We put a "safe" default size for going to cache */
3023 *expected_size = (300*1024);
3025 *expected_size = info->size;
3027 gnome_vfs_file_info_unref (info);
3029 stream = tny_vfs_stream_new (handle);
3038 TnyStream *output_stream;
3039 GtkWidget *msg_view;
3043 on_fetch_image_idle_refresh_view (gpointer userdata)
3046 FetchImageData *fidata = (FetchImageData *) userdata;
3047 g_message ("REFRESH VIEW");
3048 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3049 g_message ("QUEUING DRAW");
3050 gtk_widget_queue_draw (fidata->msg_view);
3052 g_object_unref (fidata->msg_view);
3053 g_slice_free (FetchImageData, fidata);
3058 on_fetch_image_thread (gpointer userdata)
3060 FetchImageData *fidata = (FetchImageData *) userdata;
3061 TnyStreamCache *cache;
3062 TnyStream *cache_stream;
3064 cache = modest_runtime_get_images_cache ();
3065 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3066 g_free (fidata->cache_id);
3067 g_free (fidata->uri);
3069 if (cache_stream != NULL) {
3070 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3071 tny_stream_close (cache_stream);
3072 g_object_unref (cache_stream);
3075 tny_stream_close (fidata->output_stream);
3076 g_object_unref (fidata->output_stream);
3079 gdk_threads_enter ();
3080 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3081 gdk_threads_leave ();
3087 on_fetch_image (ModestMsgView *msgview,
3090 ModestMsgViewWindow *window)
3092 const gchar *current_account;
3093 ModestMsgViewWindowPrivate *priv;
3094 FetchImageData *fidata;
3096 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3098 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3100 fidata = g_slice_new0 (FetchImageData);
3101 fidata->msg_view = g_object_ref (msgview);
3102 fidata->uri = g_strdup (uri);
3103 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3104 fidata->output_stream = g_object_ref (stream);
3106 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3107 g_object_unref (fidata->output_stream);
3108 g_free (fidata->cache_id);
3109 g_free (fidata->uri);
3110 g_object_unref (fidata->msg_view);
3111 g_slice_free (FetchImageData, fidata);
3112 tny_stream_close (stream);
3120 setup_menu (ModestMsgViewWindow *self)
3122 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3124 /* Settings menu buttons */
3125 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3126 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3127 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3128 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3129 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3130 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3132 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3133 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3134 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3135 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3136 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3137 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3139 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3140 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3141 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3142 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3143 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3144 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3146 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3147 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3148 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3149 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3150 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3151 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3155 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3157 ModestMsgViewWindowPrivate *priv;
3158 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3159 GSList *recipients = NULL;
3161 gboolean contacts_to_add = FALSE;
3163 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3167 header = modest_msg_view_window_get_header (self);
3170 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3171 g_object_unref (header);
3173 recipients = modest_tny_msg_get_all_recipients_list (msg);
3174 g_object_unref (msg);
3177 if (recipients != NULL) {
3178 GtkWidget *picker_dialog;
3179 GtkWidget *selector;
3181 gchar *selected = NULL;
3183 selector = hildon_touch_selector_new_text ();
3184 g_object_ref (selector);
3186 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3187 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3188 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3189 (const gchar *) node->data);
3190 contacts_to_add = TRUE;
3194 if (contacts_to_add) {
3197 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3198 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3200 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3201 HILDON_TOUCH_SELECTOR (selector));
3203 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3205 if (picker_result == GTK_RESPONSE_OK) {
3206 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3208 gtk_widget_destroy (picker_dialog);
3211 modest_address_book_add_address (selected);
3216 g_object_unref (selector);
3221 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3225 _modest_msg_view_window_map_event (GtkWidget *widget,
3229 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3230 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3232 if (priv->progress_hint) {
3233 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), TRUE);