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>
69 #include <modest-account-protocol.h>
71 #define MYDOCS_ENV "MYDOCSDIR"
72 #define DOCS_FOLDER ".documents"
74 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
75 struct _ModestMsgViewWindowPrivate {
78 GtkWidget *main_scroll;
79 GtkWidget *find_toolbar;
82 /* Progress observers */
83 GSList *progress_widgets;
86 GtkWidget *prev_toolitem;
87 GtkWidget *next_toolitem;
88 gboolean progress_hint;
91 /* Optimized view enabled */
92 gboolean optimized_view;
94 /* Whether this was created via the *_new_for_search_result() function. */
95 gboolean is_search_result;
97 /* Whether the message is in outbox */
100 /* A reference to the @model of the header view
101 * to allow selecting previous/next messages,
102 * if the message is currently selected in the header view.
104 const gchar *header_folder_id;
105 GtkTreeModel *header_model;
106 GtkTreeRowReference *row_reference;
107 GtkTreeRowReference *next_row_reference;
109 gulong clipboard_change_handler;
110 gulong queue_change_handler;
111 gulong account_removed_handler;
112 gulong row_changed_handler;
113 gulong row_deleted_handler;
114 gulong row_inserted_handler;
115 gulong rows_reordered_handler;
118 GtkWidget *remove_attachment_banner;
121 TnyMimePart *other_body;
126 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
127 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
128 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
129 static void modest_msg_view_window_finalize (GObject *obj);
130 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
131 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
132 ModestMsgViewWindow *obj);
133 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
134 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
137 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
139 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
140 static void modest_msg_view_window_set_zoom (ModestWindow *window,
142 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
143 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
144 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
147 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
149 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
150 gboolean show_toolbar);
152 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
154 ModestMsgViewWindow *window);
156 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
159 ModestMsgViewWindow *window);
161 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
163 ModestMsgViewWindow *window);
165 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
166 GtkTreePath *tree_path,
167 GtkTreeIter *tree_iter,
168 ModestMsgViewWindow *window);
170 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
174 ModestMsgViewWindow *window);
176 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
178 const gchar *tny_folder_id);
180 static void on_queue_changed (ModestMailOperationQueue *queue,
181 ModestMailOperation *mail_op,
182 ModestMailOperationQueueNotification type,
183 ModestMsgViewWindow *self);
185 static void on_account_removed (TnyAccountStore *account_store,
189 static void on_move_focus (GtkWidget *widget,
190 GtkDirectionType direction,
193 static void view_msg_cb (ModestMailOperation *mail_op,
200 static void set_progress_hint (ModestMsgViewWindow *self,
203 static void update_window_title (ModestMsgViewWindow *window);
205 static void init_window (ModestMsgViewWindow *obj);
207 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
209 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
211 static gboolean on_fetch_image (ModestMsgView *msgview,
214 ModestMsgViewWindow *window);
216 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
217 GtkScrollType scroll_type,
220 static gboolean message_reader (ModestMsgViewWindow *window,
221 ModestMsgViewWindowPrivate *priv,
223 const gchar *msg_uid,
225 GtkTreeRowReference *row_reference);
227 static void setup_menu (ModestMsgViewWindow *self);
228 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
231 static void update_branding (ModestMsgViewWindow *self);
233 /* list my signals */
240 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
241 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
244 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
245 MODEST_TYPE_MSG_VIEW_WINDOW, \
246 ModestMsgViewWindowPrivate))
248 static GtkWindowClass *parent_class = NULL;
250 /* uncomment the following if you have defined any signals */
251 static guint signals[LAST_SIGNAL] = {0};
254 modest_msg_view_window_get_type (void)
256 static GType my_type = 0;
258 static const GTypeInfo my_info = {
259 sizeof(ModestMsgViewWindowClass),
260 NULL, /* base init */
261 NULL, /* base finalize */
262 (GClassInitFunc) modest_msg_view_window_class_init,
263 NULL, /* class finalize */
264 NULL, /* class data */
265 sizeof(ModestMsgViewWindow),
267 (GInstanceInitFunc) modest_msg_view_window_init,
270 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
271 "ModestMsgViewWindow",
274 static const GInterfaceInfo modest_header_view_observer_info =
276 (GInterfaceInitFunc) modest_header_view_observer_init,
277 NULL, /* interface_finalize */
278 NULL /* interface_data */
281 g_type_add_interface_static (my_type,
282 MODEST_TYPE_HEADER_VIEW_OBSERVER,
283 &modest_header_view_observer_info);
289 save_state (ModestWindow *self)
291 modest_widget_memory_save (modest_runtime_get_conf (),
293 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
297 modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
298 GtkScrollType scroll_type,
302 ModestMsgViewWindowPrivate *priv;
305 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
307 switch (scroll_type) {
308 case GTK_SCROLL_STEP_UP:
311 case GTK_SCROLL_STEP_DOWN:
314 case GTK_SCROLL_PAGE_UP:
317 case GTK_SCROLL_PAGE_DOWN:
320 case GTK_SCROLL_START:
331 modest_maemo_utils_scroll_pannable((HildonPannableArea *) priv->main_scroll, 0, step);
333 return (gboolean) step;
337 add_scroll_binding (GtkBindingSet *binding_set,
339 GtkScrollType scroll)
341 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
343 gtk_binding_entry_add_signal (binding_set, keyval, 0,
345 GTK_TYPE_SCROLL_TYPE, scroll,
346 G_TYPE_BOOLEAN, FALSE);
347 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
349 GTK_TYPE_SCROLL_TYPE, scroll,
350 G_TYPE_BOOLEAN, FALSE);
354 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
356 GObjectClass *gobject_class;
357 HildonWindowClass *hildon_window_class;
358 ModestWindowClass *modest_window_class;
359 GtkBindingSet *binding_set;
361 gobject_class = (GObjectClass*) klass;
362 hildon_window_class = (HildonWindowClass *) klass;
363 modest_window_class = (ModestWindowClass *) klass;
365 parent_class = g_type_class_peek_parent (klass);
366 gobject_class->finalize = modest_msg_view_window_finalize;
368 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
369 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
370 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
371 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
372 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
373 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
375 modest_window_class->save_state_func = save_state;
377 klass->scroll_child = modest_msg_view_window_scroll_child;
379 signals[MSG_CHANGED_SIGNAL] =
380 g_signal_new ("msg-changed",
381 G_TYPE_FROM_CLASS (gobject_class),
383 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
385 modest_marshal_VOID__POINTER_POINTER,
386 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
388 signals[SCROLL_CHILD_SIGNAL] =
389 g_signal_new ("scroll-child",
390 G_TYPE_FROM_CLASS (gobject_class),
391 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
392 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
394 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
395 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
397 binding_set = gtk_binding_set_by_class (klass);
398 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
399 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
400 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
401 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
402 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
403 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
405 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
409 static void modest_header_view_observer_init(
410 ModestHeaderViewObserverIface *iface_class)
412 iface_class->update_func = modest_msg_view_window_update_model_replaced;
416 modest_msg_view_window_init (ModestMsgViewWindow *obj)
418 ModestMsgViewWindowPrivate *priv;
419 ModestWindowPrivate *parent_priv = NULL;
420 GtkActionGroup *action_group = NULL;
421 GError *error = NULL;
423 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
424 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
425 parent_priv->ui_manager = gtk_ui_manager_new();
427 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
428 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
430 /* Add common actions */
431 gtk_action_group_add_actions (action_group,
432 modest_action_entries,
433 G_N_ELEMENTS (modest_action_entries),
435 gtk_action_group_add_toggle_actions (action_group,
436 msg_view_toggle_action_entries,
437 G_N_ELEMENTS (msg_view_toggle_action_entries),
440 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
441 g_object_unref (action_group);
443 /* Load the UI definition */
444 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
447 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
448 g_error_free (error);
453 /* Add accelerators */
454 gtk_window_add_accel_group (GTK_WINDOW (obj),
455 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
457 priv->is_search_result = FALSE;
458 priv->is_outbox = FALSE;
460 priv->msg_view = NULL;
461 priv->header_model = NULL;
462 priv->header_folder_id = NULL;
463 priv->clipboard_change_handler = 0;
464 priv->queue_change_handler = 0;
465 priv->account_removed_handler = 0;
466 priv->row_changed_handler = 0;
467 priv->row_deleted_handler = 0;
468 priv->row_inserted_handler = 0;
469 priv->rows_reordered_handler = 0;
470 priv->progress_hint = FALSE;
471 priv->fetching_images = 0;
473 priv->optimized_view = FALSE;
474 priv->purge_timeout = 0;
475 priv->remove_attachment_banner = NULL;
476 priv->msg_uid = NULL;
477 priv->other_body = NULL;
479 priv->sighandlers = NULL;
482 init_window (MODEST_MSG_VIEW_WINDOW(obj));
484 hildon_program_add_window (hildon_program_get_instance(),
490 update_progress_hint (ModestMsgViewWindow *self)
492 ModestMsgViewWindowPrivate *priv;
493 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
495 if (GTK_WIDGET_VISIBLE (self)) {
496 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
497 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
502 set_progress_hint (ModestMsgViewWindow *self,
505 ModestWindowPrivate *parent_priv;
506 ModestMsgViewWindowPrivate *priv;
508 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
510 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
511 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
513 /* Sets current progress hint */
514 priv->progress_hint = enabled;
516 update_progress_hint (self);
522 init_window (ModestMsgViewWindow *obj)
524 GtkWidget *main_vbox;
525 ModestMsgViewWindowPrivate *priv;
527 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
529 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
530 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
531 main_vbox = gtk_vbox_new (FALSE, 6);
532 priv->main_scroll = hildon_pannable_area_new ();
533 g_object_set (G_OBJECT (priv->main_scroll),
534 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
537 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
538 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
539 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
541 /* NULL-ize fields if the window is destroyed */
542 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
544 gtk_widget_show_all (GTK_WIDGET(main_vbox));
548 modest_msg_view_window_disconnect_signals (ModestWindow *self)
550 ModestMsgViewWindowPrivate *priv;
551 GtkWidget *header_view = NULL;
552 GtkWindow *parent_window = NULL;
554 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
556 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
557 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
558 priv->clipboard_change_handler))
559 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
560 priv->clipboard_change_handler);
562 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
563 priv->queue_change_handler))
564 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
565 priv->queue_change_handler);
567 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
568 priv->account_removed_handler))
569 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
570 priv->account_removed_handler);
572 if (priv->header_model) {
573 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
574 priv->row_changed_handler))
575 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
576 priv->row_changed_handler);
578 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
579 priv->row_deleted_handler))
580 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
581 priv->row_deleted_handler);
583 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
584 priv->row_inserted_handler))
585 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
586 priv->row_inserted_handler);
588 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
589 priv->rows_reordered_handler))
590 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
591 priv->rows_reordered_handler);
594 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
595 priv->sighandlers = NULL;
597 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
598 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
599 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
601 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
602 MODEST_HEADER_VIEW_OBSERVER(self));
608 modest_msg_view_window_finalize (GObject *obj)
610 ModestMsgViewWindowPrivate *priv;
612 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
614 /* Sanity check: shouldn't be needed, the window mgr should
615 call this function before */
616 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
618 if (priv->other_body != NULL) {
619 g_object_unref (priv->other_body);
620 priv->other_body = NULL;
623 if (priv->header_model != NULL) {
624 g_object_unref (priv->header_model);
625 priv->header_model = NULL;
628 if (priv->remove_attachment_banner) {
629 gtk_widget_destroy (priv->remove_attachment_banner);
630 g_object_unref (priv->remove_attachment_banner);
631 priv->remove_attachment_banner = NULL;
634 if (priv->purge_timeout > 0) {
635 g_source_remove (priv->purge_timeout);
636 priv->purge_timeout = 0;
639 if (priv->row_reference) {
640 gtk_tree_row_reference_free (priv->row_reference);
641 priv->row_reference = NULL;
644 if (priv->next_row_reference) {
645 gtk_tree_row_reference_free (priv->next_row_reference);
646 priv->next_row_reference = NULL;
650 g_free (priv->msg_uid);
651 priv->msg_uid = NULL;
654 G_OBJECT_CLASS(parent_class)->finalize (obj);
658 select_next_valid_row (GtkTreeModel *model,
659 GtkTreeRowReference **row_reference,
663 GtkTreeIter tmp_iter;
665 GtkTreePath *next = NULL;
666 gboolean retval = FALSE, finished;
668 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
670 path = gtk_tree_row_reference_get_path (*row_reference);
671 gtk_tree_model_get_iter (model, &tmp_iter, path);
672 gtk_tree_row_reference_free (*row_reference);
673 *row_reference = NULL;
677 TnyHeader *header = NULL;
679 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
680 gtk_tree_model_get (model, &tmp_iter,
681 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
685 if (msg_is_visible (header, is_outbox)) {
686 next = gtk_tree_model_get_path (model, &tmp_iter);
687 *row_reference = gtk_tree_row_reference_new (model, next);
688 gtk_tree_path_free (next);
692 g_object_unref (header);
695 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
696 next = gtk_tree_model_get_path (model, &tmp_iter);
698 /* Ensure that we are not selecting the same */
699 if (gtk_tree_path_compare (path, next) != 0) {
700 gtk_tree_model_get (model, &tmp_iter,
701 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
704 if (msg_is_visible (header, is_outbox)) {
705 *row_reference = gtk_tree_row_reference_new (model, next);
709 g_object_unref (header);
713 /* If we ended up in the same message
714 then there is no valid next
718 gtk_tree_path_free (next);
720 /* If there are no more messages and we don't
721 want to start again in the first one then
722 there is no valid next message */
728 gtk_tree_path_free (path);
733 /* TODO: This should be in _init(), with the parameters as properties. */
735 modest_msg_view_window_construct (ModestMsgViewWindow *self,
736 const gchar *modest_account_name,
737 const gchar *mailbox,
738 const gchar *msg_uid)
741 ModestMsgViewWindowPrivate *priv = NULL;
742 ModestWindowPrivate *parent_priv = NULL;
743 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
744 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
746 obj = G_OBJECT (self);
747 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
748 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
750 priv->msg_uid = g_strdup (msg_uid);
753 parent_priv->menubar = NULL;
755 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
756 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
759 /* Add common dimming rules */
760 modest_dimming_rules_group_add_rules (toolbar_rules_group,
761 modest_msg_view_toolbar_dimming_entries,
762 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
763 MODEST_WINDOW (self));
764 modest_dimming_rules_group_add_rules (clipboard_rules_group,
765 modest_msg_view_clipboard_dimming_entries,
766 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
767 MODEST_WINDOW (self));
769 /* Insert dimming rules group for this window */
770 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
771 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
772 g_object_unref (toolbar_rules_group);
773 g_object_unref (clipboard_rules_group);
775 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
777 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);
778 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
779 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
780 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
781 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
782 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
783 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
784 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
785 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
786 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
787 G_CALLBACK (modest_ui_actions_on_details), obj);
788 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
789 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
790 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
791 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
792 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
793 G_CALLBACK (on_fetch_image), obj);
795 g_signal_connect (G_OBJECT (obj), "key-release-event",
796 G_CALLBACK (modest_msg_view_window_key_event),
799 g_signal_connect (G_OBJECT (obj), "key-press-event",
800 G_CALLBACK (modest_msg_view_window_key_event),
803 g_signal_connect (G_OBJECT (obj), "move-focus",
804 G_CALLBACK (on_move_focus), obj);
806 g_signal_connect (G_OBJECT (obj), "map-event",
807 G_CALLBACK (_modest_msg_view_window_map_event),
810 /* Mail Operation Queue */
811 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
813 G_CALLBACK (on_queue_changed),
816 /* Account manager */
817 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
819 G_CALLBACK(on_account_removed),
822 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
823 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
825 /* First add out toolbar ... */
826 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
828 /* ... and later the find toolbar. This way find toolbar will
829 be shown over the other */
830 priv->find_toolbar = hildon_find_toolbar_new (NULL);
831 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
832 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
833 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
834 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
835 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
836 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
837 priv->last_search = NULL;
839 /* Init the clipboard actions dim status */
840 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
842 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
847 /* FIXME: parameter checks */
849 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
850 const gchar *modest_account_name,
851 const gchar *mailbox,
852 const gchar *msg_uid,
854 GtkTreeRowReference *row_reference)
856 ModestMsgViewWindow *window = NULL;
857 ModestMsgViewWindowPrivate *priv = NULL;
858 TnyFolder *header_folder = NULL;
859 ModestHeaderView *header_view = NULL;
860 ModestWindowMgr *mgr = NULL;
863 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
866 mgr = modest_runtime_get_window_mgr ();
867 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
868 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
870 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
872 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
874 /* Remember the message list's TreeModel so we can detect changes
875 * and change the list selection when necessary: */
876 header_folder = modest_header_view_get_folder (header_view);
878 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
879 TNY_FOLDER_TYPE_OUTBOX);
880 priv->header_folder_id = tny_folder_get_id (header_folder);
881 g_object_unref(header_folder);
884 /* Setup row references and connect signals */
885 priv->header_model = g_object_ref (model);
887 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
888 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
889 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
890 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
892 priv->row_reference = NULL;
893 priv->next_row_reference = NULL;
896 /* Connect signals */
897 priv->row_changed_handler =
898 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
899 G_CALLBACK(modest_msg_view_window_on_row_changed),
901 priv->row_deleted_handler =
902 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
903 G_CALLBACK(modest_msg_view_window_on_row_deleted),
905 priv->row_inserted_handler =
906 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
907 G_CALLBACK(modest_msg_view_window_on_row_inserted),
909 priv->rows_reordered_handler =
910 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
911 G_CALLBACK(modest_msg_view_window_on_row_reordered),
914 if (header_view != NULL){
915 modest_header_view_add_observer(header_view,
916 MODEST_HEADER_VIEW_OBSERVER(window));
919 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
920 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
921 update_branding (MODEST_MSG_VIEW_WINDOW (window));
923 /* gtk_widget_show_all (GTK_WIDGET (window)); */
924 modest_msg_view_window_update_priority (window);
925 /* Check dimming rules */
926 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
927 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
928 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
930 return MODEST_WINDOW(window);
934 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
935 const gchar *mailbox,
936 const gchar *msg_uid)
938 ModestMsgViewWindow *window = NULL;
939 ModestMsgViewWindowPrivate *priv = NULL;
940 ModestWindowMgr *mgr = NULL;
942 TnyAccount *account = NULL;
944 mgr = modest_runtime_get_window_mgr ();
945 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
946 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
948 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
950 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
954 is_merge = g_str_has_prefix (msg_uid, "merge:");
956 /* Get the account */
958 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
962 if (is_merge || account) {
963 TnyFolder *folder = NULL;
965 /* Try to get the message, if it's already downloaded
966 we don't need to connect */
968 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
970 ModestTnyAccountStore *account_store;
971 ModestTnyLocalFoldersAccount *local_folders_account;
973 account_store = modest_runtime_get_account_store ();
974 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
975 modest_tny_account_store_get_local_folders_account (account_store));
976 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
977 g_object_unref (local_folders_account);
981 gboolean device_online;
983 device = modest_runtime_get_device();
984 device_online = tny_device_is_online (device);
986 message_reader (window, priv, NULL, msg_uid, folder, NULL);
988 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
990 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
991 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
992 update_branding (MODEST_MSG_VIEW_WINDOW (window));
993 g_object_unref (msg);
995 message_reader (window, priv, NULL, msg_uid, folder, NULL);
998 g_object_unref (folder);
1003 /* Check dimming rules */
1004 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1005 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1006 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1008 return MODEST_WINDOW(window);
1012 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1013 const gchar *modest_account_name,
1014 const gchar *mailbox,
1015 const gchar *msg_uid,
1016 GtkTreeRowReference *row_reference)
1018 ModestMsgViewWindow *window = NULL;
1019 ModestMsgViewWindowPrivate *priv = NULL;
1020 TnyFolder *header_folder = NULL;
1021 ModestWindowMgr *mgr = NULL;
1025 mgr = modest_runtime_get_window_mgr ();
1026 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1027 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1029 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1031 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1033 /* Remember the message list's TreeModel so we can detect changes
1034 * and change the list selection when necessary: */
1036 if (header_view != NULL){
1037 header_folder = modest_header_view_get_folder(header_view);
1038 /* This could happen if the header folder was
1039 unseleted before opening this msg window (for
1040 example if the user selects an account in the
1041 folder view of the main window */
1042 if (header_folder) {
1043 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1044 TNY_FOLDER_TYPE_OUTBOX);
1045 priv->header_folder_id = tny_folder_get_id(header_folder);
1046 g_object_unref(header_folder);
1050 /* Setup row references and connect signals */
1051 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1052 g_object_ref (priv->header_model);
1054 if (row_reference && gtk_tree_row_reference_valid (row_reference)) {
1055 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1056 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1057 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1059 priv->row_reference = NULL;
1060 priv->next_row_reference = NULL;
1063 /* Connect signals */
1064 priv->row_changed_handler =
1065 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1066 G_CALLBACK(modest_msg_view_window_on_row_changed),
1068 priv->row_deleted_handler =
1069 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1070 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1072 priv->row_inserted_handler =
1073 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1074 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1076 priv->rows_reordered_handler =
1077 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1078 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1081 if (header_view != NULL){
1082 modest_header_view_add_observer(header_view,
1083 MODEST_HEADER_VIEW_OBSERVER(window));
1086 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1087 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1089 if (priv->row_reference) {
1090 path = gtk_tree_row_reference_get_path (priv->row_reference);
1091 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1093 gtk_tree_model_get (priv->header_model, &iter,
1094 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1096 message_reader (window, priv, header, NULL, NULL, priv->row_reference);
1097 g_object_unref (header);
1099 gtk_tree_path_free (path);
1101 /* Check dimming rules */
1102 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1103 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1104 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1106 return MODEST_WINDOW(window);
1110 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1111 const gchar *modest_account_name,
1112 const gchar *mailbox,
1113 const gchar *msg_uid)
1115 ModestMsgViewWindow *window = NULL;
1116 ModestMsgViewWindowPrivate *priv = NULL;
1117 ModestWindowMgr *mgr = NULL;
1119 mgr = modest_runtime_get_window_mgr ();
1120 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1121 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1122 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1124 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1126 /* Remember that this is a search result,
1127 * so we can disable some UI appropriately: */
1128 priv->is_search_result = TRUE;
1130 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1131 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1133 update_window_title (window);
1134 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1135 modest_msg_view_window_update_priority (window);
1137 /* Check dimming rules */
1138 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1139 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1140 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1142 return MODEST_WINDOW(window);
1146 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1148 ModestMsgViewWindowPrivate *priv = NULL;
1150 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1151 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1153 return (priv->other_body != NULL);
1157 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1158 TnyMimePart *other_body,
1159 const gchar *modest_account_name,
1160 const gchar *mailbox,
1161 const gchar *msg_uid)
1163 GObject *obj = NULL;
1164 ModestMsgViewWindowPrivate *priv;
1165 ModestWindowMgr *mgr = NULL;
1167 g_return_val_if_fail (msg, NULL);
1168 mgr = modest_runtime_get_window_mgr ();
1169 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1170 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1171 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1172 modest_account_name, mailbox, msg_uid);
1175 priv->other_body = g_object_ref (other_body);
1176 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1178 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1180 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1181 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1183 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1185 /* Check dimming rules */
1186 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1187 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1188 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1190 return MODEST_WINDOW(obj);
1194 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1195 const gchar *modest_account_name,
1196 const gchar *mailbox,
1197 const gchar *msg_uid)
1199 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1203 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1206 ModestMsgViewWindow *window)
1208 check_dimming_rules_after_change (window);
1212 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1214 ModestMsgViewWindow *window)
1216 check_dimming_rules_after_change (window);
1218 /* The window could have dissapeared */
1221 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1223 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1224 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1228 /* On insertions we check if the folder still has the message we are
1229 * showing or do not. If do not, we do nothing. Which means we are still
1230 * not attached to any header folder and thus next/prev buttons are
1231 * still dimmed. Once the message that is shown by msg-view is found, the
1232 * new model of header-view will be attached and the references will be set.
1233 * On each further insertions dimming rules will be checked. However
1234 * this requires extra CPU time at least works.
1235 * (An message might be deleted from TnyFolder and thus will not be
1236 * inserted into the model again for example if it is removed by the
1237 * imap server and the header view is refreshed.)
1240 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1241 GtkTreePath *tree_path,
1242 GtkTreeIter *tree_iter,
1243 ModestMsgViewWindow *window)
1245 ModestMsgViewWindowPrivate *priv = NULL;
1246 TnyHeader *header = NULL;
1248 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1249 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1251 g_assert (model == priv->header_model);
1253 /* Check if the newly inserted message is the same we are actually
1254 * showing. IF not, we should remain detached from the header model
1255 * and thus prev and next toolbar buttons should remain dimmed. */
1256 gtk_tree_model_get (model, tree_iter,
1257 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1260 if (TNY_IS_HEADER (header)) {
1263 uid = modest_tny_folder_get_header_unique_id (header);
1264 if (!g_str_equal(priv->msg_uid, uid)) {
1265 check_dimming_rules_after_change (window);
1267 g_object_unref (G_OBJECT(header));
1271 g_object_unref(G_OBJECT(header));
1274 if (priv->row_reference) {
1275 gtk_tree_row_reference_free (priv->row_reference);
1278 /* Setup row_reference for the actual msg. */
1279 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1280 if (priv->row_reference == NULL) {
1281 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1285 /* Now set up next_row_reference. */
1286 if (priv->next_row_reference) {
1287 gtk_tree_row_reference_free (priv->next_row_reference);
1290 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1291 select_next_valid_row (priv->header_model,
1292 &(priv->next_row_reference), FALSE, priv->is_outbox);
1294 /* Connect the remaining callbacks to become able to detect
1295 * changes in header-view. */
1296 priv->row_changed_handler =
1297 g_signal_connect (priv->header_model, "row-changed",
1298 G_CALLBACK (modest_msg_view_window_on_row_changed),
1300 priv->row_deleted_handler =
1301 g_signal_connect (priv->header_model, "row-deleted",
1302 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1304 priv->rows_reordered_handler =
1305 g_signal_connect (priv->header_model, "rows-reordered",
1306 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1309 check_dimming_rules_after_change (window);
1313 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1317 ModestMsgViewWindow *window)
1319 ModestMsgViewWindowPrivate *priv = NULL;
1320 gboolean already_changed = FALSE;
1322 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1324 /* If the current row was reordered select the proper next
1325 valid row. The same if the next row reference changes */
1326 if (!priv->row_reference ||
1327 !gtk_tree_row_reference_valid (priv->row_reference))
1330 if (priv->next_row_reference &&
1331 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1332 GtkTreePath *cur, *next;
1333 /* Check that the order is still the correct one */
1334 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1335 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1336 gtk_tree_path_next (cur);
1337 if (gtk_tree_path_compare (cur, next) != 0) {
1338 gtk_tree_row_reference_free (priv->next_row_reference);
1339 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1340 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1341 already_changed = TRUE;
1343 gtk_tree_path_free (cur);
1344 gtk_tree_path_free (next);
1346 if (priv->next_row_reference)
1347 gtk_tree_row_reference_free (priv->next_row_reference);
1348 /* Update next row reference */
1349 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1350 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1351 already_changed = TRUE;
1354 check_dimming_rules_after_change (window);
1357 /* The modest_msg_view_window_update_model_replaced implements update
1358 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1359 * actually belongs to the header-view is the same as the TnyFolder of
1360 * the message of msg-view or not. If they are different, there is
1361 * nothing to do. If they are the same, then the model has replaced and
1362 * the reference in msg-view shall be replaced from the old model to
1363 * the new model. In this case the view will be detached from it's
1364 * header folder. From this point the next/prev buttons are dimmed.
1367 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1368 GtkTreeModel *model,
1369 const gchar *tny_folder_id)
1371 ModestMsgViewWindowPrivate *priv = NULL;
1372 ModestMsgViewWindow *window = NULL;
1374 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1375 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1377 window = MODEST_MSG_VIEW_WINDOW(observer);
1378 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1380 /* If there is an other folder in the header-view then we do
1381 * not care about it's model (msg list). Else if the
1382 * header-view shows the folder the msg shown by us is in, we
1383 * shall replace our model reference and make some check. */
1384 if(model == NULL || tny_folder_id == NULL ||
1385 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1388 /* Model is changed(replaced), so we should forget the old
1389 * one. Because there might be other references and there
1390 * might be some change on the model even if we unreferenced
1391 * it, we need to disconnect our signals here. */
1392 if (priv->header_model) {
1393 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1394 priv->row_changed_handler))
1395 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1396 priv->row_changed_handler);
1397 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1398 priv->row_deleted_handler))
1399 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1400 priv->row_deleted_handler);
1401 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1402 priv->row_inserted_handler))
1403 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1404 priv->row_inserted_handler);
1405 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1406 priv->rows_reordered_handler))
1407 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1408 priv->rows_reordered_handler);
1411 if (priv->row_reference)
1412 gtk_tree_row_reference_free (priv->row_reference);
1413 if (priv->next_row_reference)
1414 gtk_tree_row_reference_free (priv->next_row_reference);
1415 g_object_unref(priv->header_model);
1418 priv->row_changed_handler = 0;
1419 priv->row_deleted_handler = 0;
1420 priv->row_inserted_handler = 0;
1421 priv->rows_reordered_handler = 0;
1422 priv->next_row_reference = NULL;
1423 priv->row_reference = NULL;
1424 priv->header_model = NULL;
1427 priv->header_model = g_object_ref (model);
1429 /* Also we must connect to the new model for row insertions.
1430 * Only for insertions now. We will need other ones only after
1431 * the msg is show by msg-view is added to the new model. */
1432 priv->row_inserted_handler =
1433 g_signal_connect (priv->header_model, "row-inserted",
1434 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1437 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1438 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1442 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1444 ModestMsgViewWindowPrivate *priv= NULL;
1446 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1447 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1449 return priv->progress_hint;
1453 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1455 ModestMsgViewWindowPrivate *priv= NULL;
1457 TnyHeader *header = NULL;
1458 GtkTreePath *path = NULL;
1461 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1462 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1464 /* If the message was not obtained from a treemodel,
1465 * for instance if it was opened directly by the search UI:
1467 if (priv->header_model == NULL ||
1468 priv->row_reference == NULL ||
1469 !gtk_tree_row_reference_valid (priv->row_reference)) {
1470 msg = modest_msg_view_window_get_message (self);
1472 header = tny_msg_get_header (msg);
1473 g_object_unref (msg);
1478 /* Get iter of the currently selected message in the header view: */
1479 path = gtk_tree_row_reference_get_path (priv->row_reference);
1480 g_return_val_if_fail (path != NULL, NULL);
1481 gtk_tree_model_get_iter (priv->header_model,
1485 /* Get current message header */
1486 gtk_tree_model_get (priv->header_model, &iter,
1487 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1490 gtk_tree_path_free (path);
1495 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1497 ModestMsgViewWindowPrivate *priv;
1499 g_return_val_if_fail (self, NULL);
1501 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1503 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1507 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1509 ModestMsgViewWindowPrivate *priv;
1511 g_return_val_if_fail (self, NULL);
1513 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1515 return (const gchar*) priv->msg_uid;
1518 /* Used for the Ctrl+F accelerator */
1520 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1523 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1524 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1526 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1527 modest_msg_view_window_find_toolbar_close (obj, data);
1529 modest_msg_view_window_show_find_toolbar (obj, data);
1533 /* Handler for menu option */
1535 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1538 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1539 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1541 gtk_widget_show (priv->find_toolbar);
1542 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1545 /* Handler for click on the "X" close button in find toolbar */
1547 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1548 ModestMsgViewWindow *obj)
1550 ModestMsgViewWindowPrivate *priv;
1552 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1555 gtk_widget_hide (priv->find_toolbar);
1556 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1560 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1561 ModestMsgViewWindow *obj)
1563 gchar *current_search;
1564 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1566 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1567 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1571 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1573 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1574 g_free (current_search);
1575 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1579 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1581 g_free (priv->last_search);
1582 priv->last_search = g_strdup (current_search);
1583 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1586 hildon_banner_show_information (NULL, NULL,
1587 _HL("ckct_ib_find_no_matches"));
1588 g_free (priv->last_search);
1589 priv->last_search = NULL;
1591 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1594 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1595 hildon_banner_show_information (NULL, NULL,
1596 _HL("ckct_ib_find_search_complete"));
1597 g_free (priv->last_search);
1598 priv->last_search = NULL;
1600 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1604 g_free (current_search);
1609 modest_msg_view_window_set_zoom (ModestWindow *window,
1612 ModestMsgViewWindowPrivate *priv;
1613 ModestWindowPrivate *parent_priv;
1615 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1617 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1618 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1619 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1624 modest_msg_view_window_get_zoom (ModestWindow *window)
1626 ModestMsgViewWindowPrivate *priv;
1628 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1630 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1631 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1635 modest_msg_view_window_zoom_plus (ModestWindow *window)
1638 ModestMsgViewWindowPrivate *priv;
1642 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1643 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1645 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1647 if (zoom_level >= 2.0) {
1648 hildon_banner_show_information (NULL, NULL,
1649 _CS("ckct_ib_max_zoom_level_reached"));
1651 } else if (zoom_level >= 1.5) {
1653 } else if (zoom_level >= 1.2) {
1655 } else if (zoom_level >= 1.0) {
1657 } else if (zoom_level >= 0.8) {
1659 } else if (zoom_level >= 0.5) {
1665 /* set zoom level */
1666 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1667 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1668 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1669 g_free (banner_text);
1670 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1676 modest_msg_view_window_zoom_minus (ModestWindow *window)
1679 ModestMsgViewWindowPrivate *priv;
1683 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1684 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1686 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1688 if (zoom_level <= 0.5) {
1689 hildon_banner_show_information (NULL, NULL,
1690 _CS("ckct_ib_min_zoom_level_reached"));
1692 } else if (zoom_level <= 0.8) {
1694 } else if (zoom_level <= 1.0) {
1696 } else if (zoom_level <= 1.2) {
1698 } else if (zoom_level <= 1.5) {
1700 } else if (zoom_level <= 2.0) {
1706 /* set zoom level */
1707 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1708 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1709 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1710 g_free (banner_text);
1711 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1717 modest_msg_view_window_key_event (GtkWidget *window,
1723 focus = gtk_window_get_focus (GTK_WINDOW (window));
1725 /* for the find toolbar case */
1726 if (focus && GTK_IS_ENTRY (focus)) {
1727 if (event->keyval == GDK_BackSpace) {
1729 copy = gdk_event_copy ((GdkEvent *) event);
1730 gtk_widget_event (focus, copy);
1731 gdk_event_free (copy);
1741 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1744 ModestMsgViewWindowPrivate *priv;
1745 GtkTreeIter tmp_iter;
1746 gboolean is_last_selected;
1748 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1749 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1751 /*if no model (so no rows at all), then virtually we are the last*/
1752 if (!priv->header_model || !priv->row_reference)
1755 if (!gtk_tree_row_reference_valid (priv->row_reference))
1758 path = gtk_tree_row_reference_get_path (priv->row_reference);
1762 is_last_selected = TRUE;
1763 while (is_last_selected) {
1765 gtk_tree_path_next (path);
1766 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1768 gtk_tree_model_get (priv->header_model, &tmp_iter,
1769 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1772 if (msg_is_visible (header, priv->is_outbox))
1773 is_last_selected = FALSE;
1774 g_object_unref(G_OBJECT(header));
1777 gtk_tree_path_free (path);
1778 return is_last_selected;
1782 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1784 ModestMsgViewWindowPrivate *priv;
1786 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1787 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1789 return priv->header_model != NULL;
1793 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1795 ModestMsgViewWindowPrivate *priv;
1797 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1798 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1800 return priv->is_search_result;
1804 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1806 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1808 if (!check_outbox) {
1811 ModestTnySendQueueStatus status;
1812 status = modest_tny_all_send_queues_get_msg_status (header);
1813 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1814 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1819 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1822 ModestMsgViewWindowPrivate *priv;
1823 gboolean is_first_selected;
1824 GtkTreeIter tmp_iter;
1826 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1827 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1829 /*if no model (so no rows at all), then virtually we are the first*/
1830 if (!priv->header_model || !priv->row_reference)
1833 if (!gtk_tree_row_reference_valid (priv->row_reference))
1836 path = gtk_tree_row_reference_get_path (priv->row_reference);
1840 is_first_selected = TRUE;
1841 while (is_first_selected) {
1843 if(!gtk_tree_path_prev (path))
1845 /* Here the 'if' is needless for logic, but let make sure
1846 * iter is valid for gtk_tree_model_get. */
1847 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1849 gtk_tree_model_get (priv->header_model, &tmp_iter,
1850 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1853 if (msg_is_visible (header, priv->is_outbox))
1854 is_first_selected = FALSE;
1855 g_object_unref(G_OBJECT(header));
1858 gtk_tree_path_free (path);
1859 return is_first_selected;
1866 GtkTreeRowReference *row_reference;
1870 message_reader_performer (gboolean canceled,
1872 GtkWindow *parent_window,
1873 TnyAccount *account,
1876 ModestMailOperation *mail_op = NULL;
1877 MsgReaderInfo *info;
1879 info = (MsgReaderInfo *) user_data;
1880 if (canceled || err) {
1881 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1885 /* Register the header - it'll be unregistered in the callback */
1887 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1889 /* New mail operation */
1890 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1891 modest_ui_actions_disk_operations_error_handler,
1894 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1896 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1898 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1899 g_object_unref (mail_op);
1901 /* Update dimming rules */
1902 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1903 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1906 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1907 g_free (info->msg_uid);
1909 g_object_unref (info->folder);
1911 g_object_unref (info->header);
1912 g_slice_free (MsgReaderInfo, info);
1917 * Reads the message whose summary item is @header. It takes care of
1918 * several things, among others:
1920 * If the message was not previously downloaded then ask the user
1921 * before downloading. If there is no connection launch the connection
1922 * dialog. Update toolbar dimming rules.
1924 * Returns: TRUE if the mail operation was started, otherwise if the
1925 * user do not want to download the message, or if the user do not
1926 * want to connect, then the operation is not issued
1929 message_reader (ModestMsgViewWindow *window,
1930 ModestMsgViewWindowPrivate *priv,
1932 const gchar *msg_uid,
1934 GtkTreeRowReference *row_reference)
1936 ModestWindowMgr *mgr;
1937 TnyAccount *account = NULL;
1938 MsgReaderInfo *info;
1940 /* We set the header from model while we're loading */
1941 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1942 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1948 g_object_ref (folder);
1950 mgr = modest_runtime_get_window_mgr ();
1951 /* Msg download completed */
1952 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1954 /* Ask the user if he wants to download the message if
1956 if (!tny_device_is_online (modest_runtime_get_device())) {
1957 GtkResponseType response;
1959 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1960 _("mcen_nc_get_msg"));
1961 if (response == GTK_RESPONSE_CANCEL) {
1962 update_window_title (window);
1967 folder = tny_header_get_folder (header);
1969 info = g_slice_new (MsgReaderInfo);
1970 info->msg_uid = g_strdup (msg_uid);
1972 info->header = g_object_ref (header);
1974 info->header = NULL;
1976 info->folder = g_object_ref (folder);
1978 info->folder = NULL;
1979 if (row_reference) {
1980 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1982 info->row_reference = NULL;
1985 /* Offer the connection dialog if necessary */
1986 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1988 TNY_FOLDER_STORE (folder),
1989 message_reader_performer,
1992 g_object_unref (folder);
1998 folder = tny_header_get_folder (header);
2001 account = tny_folder_get_account (folder);
2003 info = g_slice_new (MsgReaderInfo);
2004 info->msg_uid = g_strdup (msg_uid);
2006 info->folder = g_object_ref (folder);
2008 info->folder = NULL;
2010 info->header = g_object_ref (header);
2012 info->header = NULL;
2014 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2016 info->row_reference = NULL;
2018 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2020 g_object_unref (account);
2022 g_object_unref (folder);
2028 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2030 ModestMsgViewWindowPrivate *priv;
2031 GtkTreePath *path= NULL;
2032 GtkTreeIter tmp_iter;
2034 gboolean retval = TRUE;
2035 GtkTreeRowReference *row_reference = NULL;
2037 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2038 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2040 if (!priv->row_reference)
2043 /* Update the next row reference if it's not valid. This could
2044 happen if for example the header which it was pointing to,
2045 was deleted. The best place to do it is in the row-deleted
2046 handler but the tinymail model do not work like the glib
2047 tree models and reports the deletion when the row is still
2049 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2050 if (priv->next_row_reference) {
2051 gtk_tree_row_reference_free (priv->next_row_reference);
2053 if (gtk_tree_row_reference_valid (priv->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), FALSE, priv->is_outbox);
2057 priv->next_row_reference = NULL;
2060 if (priv->next_row_reference)
2061 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2065 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2067 gtk_tree_model_get_iter (priv->header_model,
2070 gtk_tree_path_free (path);
2072 gtk_tree_model_get (priv->header_model, &tmp_iter,
2073 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2076 /* Read the message & show it */
2077 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2080 gtk_tree_row_reference_free (row_reference);
2083 g_object_unref (header);
2089 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2091 ModestMsgViewWindowPrivate *priv = NULL;
2093 gboolean finished = FALSE;
2094 gboolean retval = FALSE;
2096 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2097 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2099 if (priv->row_reference && !gtk_tree_row_reference_valid (priv->row_reference)) {
2100 gtk_tree_row_reference_free (priv->row_reference);
2101 priv->row_reference = NULL;
2104 /* Return inmediatly if there is no header model */
2105 if (!priv->header_model || !priv->row_reference)
2108 path = gtk_tree_row_reference_get_path (priv->row_reference);
2109 while (!finished && gtk_tree_path_prev (path)) {
2113 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2114 gtk_tree_model_get (priv->header_model, &iter,
2115 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2119 if (msg_is_visible (header, priv->is_outbox)) {
2120 GtkTreeRowReference *row_reference;
2121 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2122 /* Read the message & show it */
2123 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2124 gtk_tree_row_reference_free (row_reference);
2128 g_object_unref (header);
2132 gtk_tree_path_free (path);
2137 view_msg_cb (ModestMailOperation *mail_op,
2144 ModestMsgViewWindow *self = NULL;
2145 ModestMsgViewWindowPrivate *priv = NULL;
2146 GtkTreeRowReference *row_reference = NULL;
2148 /* Unregister the header (it was registered before creating the mail operation) */
2149 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2151 row_reference = (GtkTreeRowReference *) user_data;
2154 gtk_tree_row_reference_free (row_reference);
2155 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2157 /* Restore window title */
2158 update_window_title (self);
2159 g_object_unref (self);
2164 /* If there was any error */
2165 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2167 gtk_tree_row_reference_free (row_reference);
2168 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2170 /* Restore window title */
2171 update_window_title (self);
2172 g_object_unref (self);
2177 /* Get the window */
2178 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2179 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2180 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2182 /* Update the row reference */
2183 if (priv->row_reference != NULL) {
2184 gtk_tree_row_reference_free (priv->row_reference);
2185 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2186 if (priv->next_row_reference != NULL) {
2187 gtk_tree_row_reference_free (priv->next_row_reference);
2189 if (priv->row_reference) {
2190 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2191 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2193 priv->next_row_reference = NULL;
2197 /* Mark header as read */
2198 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2199 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2201 /* Set new message */
2202 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2203 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2204 modest_msg_view_window_update_priority (self);
2205 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2206 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2207 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2210 /* Set the new message uid of the window */
2211 if (priv->msg_uid) {
2212 g_free (priv->msg_uid);
2213 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2216 /* Notify the observers */
2217 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2218 0, priv->header_model, priv->row_reference);
2221 g_object_unref (self);
2223 gtk_tree_row_reference_free (row_reference);
2227 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2229 ModestMsgViewWindowPrivate *priv;
2231 TnyFolderType folder_type;
2233 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2235 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2237 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2241 folder = tny_msg_get_folder (msg);
2243 folder_type = modest_tny_folder_guess_folder_type (folder);
2244 g_object_unref (folder);
2246 g_object_unref (msg);
2254 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2256 ModestMsgViewWindowPrivate *priv;
2257 TnyHeader *header = NULL;
2258 TnyHeaderFlags flags = 0;
2260 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2262 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2264 GtkTreePath *path = NULL;
2266 path = gtk_tree_row_reference_get_path (priv->row_reference);
2267 g_return_if_fail (path != NULL);
2268 gtk_tree_model_get_iter (priv->header_model,
2270 gtk_tree_row_reference_get_path (priv->row_reference));
2272 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2274 gtk_tree_path_free (path);
2277 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2279 header = tny_msg_get_header (msg);
2280 g_object_unref (msg);
2285 flags = tny_header_get_flags (header);
2286 g_object_unref(G_OBJECT(header));
2289 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2294 toolbar_resize (ModestMsgViewWindow *self)
2296 ModestMsgViewWindowPrivate *priv = NULL;
2297 ModestWindowPrivate *parent_priv = NULL;
2299 gint static_button_size;
2300 ModestWindowMgr *mgr;
2302 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2303 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2304 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2306 mgr = modest_runtime_get_window_mgr ();
2307 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2309 if (parent_priv->toolbar) {
2310 /* Set expandable and homogeneous tool buttons */
2311 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2312 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2313 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2314 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2315 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2316 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2317 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2318 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2319 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2320 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2321 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2322 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2323 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2324 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2325 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2326 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2327 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2328 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2329 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2334 modest_msg_view_window_show_toolbar (ModestWindow *self,
2335 gboolean show_toolbar)
2337 ModestMsgViewWindowPrivate *priv = NULL;
2338 ModestWindowPrivate *parent_priv;
2340 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2341 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2343 /* Set optimized view status */
2344 priv->optimized_view = !show_toolbar;
2346 if (!parent_priv->toolbar) {
2347 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2349 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2350 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2352 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2353 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2354 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2357 hildon_window_add_toolbar (HILDON_WINDOW (self),
2358 GTK_TOOLBAR (parent_priv->toolbar));
2363 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2364 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2365 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2367 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2368 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2369 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2371 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2374 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2375 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2380 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2382 ModestMsgViewWindow *window)
2384 if (!GTK_WIDGET_VISIBLE (window))
2387 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2391 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2393 ModestMsgViewWindowPrivate *priv;
2395 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2396 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2398 return priv->progress_hint;
2402 observers_empty (ModestMsgViewWindow *self)
2405 ModestMsgViewWindowPrivate *priv;
2406 gboolean is_empty = TRUE;
2407 guint pending_ops = 0;
2409 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2410 tmp = priv->progress_widgets;
2412 /* Check all observers */
2413 while (tmp && is_empty) {
2414 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2415 is_empty = pending_ops == 0;
2417 tmp = g_slist_next(tmp);
2424 on_account_removed (TnyAccountStore *account_store,
2425 TnyAccount *account,
2428 /* Do nothing if it's a transport account, because we only
2429 show the messages of a store account */
2430 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2431 const gchar *parent_acc = NULL;
2432 const gchar *our_acc = NULL;
2434 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2435 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2437 /* Close this window if I'm showing a message of the removed account */
2438 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2439 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2444 on_mail_operation_started (ModestMailOperation *mail_op,
2447 ModestMsgViewWindow *self;
2448 ModestMailOperationTypeOperation op_type;
2450 ModestMsgViewWindowPrivate *priv;
2451 GObject *source = NULL;
2453 self = MODEST_MSG_VIEW_WINDOW (user_data);
2454 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2455 op_type = modest_mail_operation_get_type_operation (mail_op);
2456 tmp = priv->progress_widgets;
2457 source = modest_mail_operation_get_source(mail_op);
2458 if (G_OBJECT (self) == source) {
2459 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2460 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2461 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2462 set_progress_hint (self, TRUE);
2464 modest_progress_object_add_operation (
2465 MODEST_PROGRESS_OBJECT (tmp->data),
2467 tmp = g_slist_next (tmp);
2471 g_object_unref (source);
2473 /* Update dimming rules */
2474 check_dimming_rules_after_change (self);
2478 on_mail_operation_finished (ModestMailOperation *mail_op,
2481 ModestMsgViewWindow *self;
2482 ModestMailOperationTypeOperation op_type;
2484 ModestMsgViewWindowPrivate *priv;
2486 self = MODEST_MSG_VIEW_WINDOW (user_data);
2487 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2488 op_type = modest_mail_operation_get_type_operation (mail_op);
2489 tmp = priv->progress_widgets;
2491 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2492 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2493 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2495 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2497 tmp = g_slist_next (tmp);
2500 /* If no more operations are being observed, NORMAL mode is enabled again */
2501 if (observers_empty (self)) {
2502 set_progress_hint (self, FALSE);
2506 /* Update dimming rules. We have to do this right here
2507 and not in view_msg_cb because at that point the
2508 transfer mode is still enabled so the dimming rule
2509 won't let the user delete the message that has been
2510 readed for example */
2511 check_dimming_rules_after_change (self);
2515 on_queue_changed (ModestMailOperationQueue *queue,
2516 ModestMailOperation *mail_op,
2517 ModestMailOperationQueueNotification type,
2518 ModestMsgViewWindow *self)
2520 ModestMsgViewWindowPrivate *priv;
2522 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2524 /* If this operations was created by another window, do nothing */
2525 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2528 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2529 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2531 "operation-started",
2532 G_CALLBACK (on_mail_operation_started),
2534 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2536 "operation-finished",
2537 G_CALLBACK (on_mail_operation_finished),
2539 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2540 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2542 "operation-started");
2543 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2545 "operation-finished");
2550 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2552 ModestMsgViewWindowPrivate *priv;
2553 TnyList *selected_attachments = NULL;
2555 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2556 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2558 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2559 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2561 return selected_attachments;
2565 ModestMsgViewWindow *self;
2567 } DecodeAsyncHelper;
2570 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2576 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2578 /* It could happen that the window was closed */
2579 if (GTK_WIDGET_VISIBLE (helper->self))
2580 set_progress_hint (helper->self, FALSE);
2582 if (cancelled || err) {
2584 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2585 modest_platform_information_banner (NULL, NULL, msg);
2591 /* make the file read-only */
2592 g_chmod(helper->file_path, 0444);
2594 /* Activate the file */
2595 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2599 g_object_unref (helper->self);
2600 g_free (helper->file_path);
2601 g_slice_free (DecodeAsyncHelper, helper);
2605 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2606 TnyMimePart *mime_part)
2608 ModestMsgViewWindowPrivate *priv;
2609 const gchar *msg_uid;
2610 gchar *attachment_uid = NULL;
2611 gint attachment_index = 0;
2612 TnyList *attachments;
2614 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2615 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2616 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2618 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2619 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2620 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2621 g_object_unref (attachments);
2623 if (msg_uid && attachment_index >= 0) {
2624 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2627 if (mime_part == NULL) {
2628 gboolean error = FALSE;
2629 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2630 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2632 } else if (tny_list_get_length (selected_attachments) > 1) {
2633 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2637 iter = tny_list_create_iterator (selected_attachments);
2638 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2639 g_object_unref (iter);
2641 if (selected_attachments)
2642 g_object_unref (selected_attachments);
2647 g_object_ref (mime_part);
2650 if (tny_mime_part_is_purged (mime_part))
2653 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2654 gchar *filepath = NULL;
2655 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2656 gboolean show_error_banner = FALSE;
2657 TnyFsStream *temp_stream = NULL;
2658 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2661 if (temp_stream != NULL) {
2662 ModestAccountMgr *mgr;
2663 DecodeAsyncHelper *helper;
2664 gboolean decode_in_provider;
2665 ModestProtocol *protocol;
2666 const gchar *account;
2668 /* Activate progress hint */
2669 set_progress_hint (window, TRUE);
2671 helper = g_slice_new0 (DecodeAsyncHelper);
2672 helper->self = g_object_ref (window);
2673 helper->file_path = g_strdup (filepath);
2675 decode_in_provider = FALSE;
2676 mgr = modest_runtime_get_account_mgr ();
2677 account = modest_window_get_active_account (MODEST_WINDOW (window));
2678 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2679 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2680 decode_in_provider =
2681 modest_account_protocol_decode_part_to_stream_async (
2682 MODEST_ACCOUNT_PROTOCOL (protocol),
2685 TNY_STREAM (temp_stream),
2686 on_decode_to_stream_async_handler,
2692 if (!decode_in_provider)
2693 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2694 on_decode_to_stream_async_handler,
2697 g_object_unref (temp_stream);
2698 /* NOTE: files in the temporary area will be automatically
2699 * cleaned after some time if they are no longer in use */
2702 const gchar *content_type;
2703 /* the file may already exist but it isn't writable,
2704 * let's try to open it anyway */
2705 content_type = tny_mime_part_get_content_type (mime_part);
2706 modest_platform_activate_file (filepath, content_type);
2708 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2709 show_error_banner = TRUE;
2714 if (show_error_banner)
2715 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2716 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2717 ModestWindowMgr *mgr;
2718 ModestWindow *msg_win = NULL;
2719 TnyMsg *current_msg;
2723 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2724 mgr = modest_runtime_get_window_mgr ();
2725 header = tny_msg_get_header (TNY_MSG (current_msg));
2726 found = modest_window_mgr_find_registered_message_uid (mgr,
2731 g_debug ("window for this body is already being created");
2734 /* it's not found, so create a new window for it */
2735 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2736 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2737 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2739 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2741 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2742 account, mailbox, attachment_uid);
2744 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2745 modest_window_get_zoom (MODEST_WINDOW (window)));
2746 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2747 gtk_widget_show_all (GTK_WIDGET (msg_win));
2749 gtk_widget_destroy (GTK_WIDGET (msg_win));
2751 g_object_unref (current_msg);
2753 /* message attachment */
2754 TnyHeader *header = NULL;
2755 ModestWindowMgr *mgr;
2756 ModestWindow *msg_win = NULL;
2759 header = tny_msg_get_header (TNY_MSG (mime_part));
2760 mgr = modest_runtime_get_window_mgr ();
2761 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2764 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2765 * thus, we don't do anything */
2766 g_debug ("window for is already being created");
2768 /* it's not found, so create a new window for it */
2769 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2770 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2771 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2773 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2774 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2775 mailbox, attachment_uid);
2776 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2777 modest_window_get_zoom (MODEST_WINDOW (window)));
2778 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2779 gtk_widget_show_all (GTK_WIDGET (msg_win));
2781 gtk_widget_destroy (GTK_WIDGET (msg_win));
2787 g_free (attachment_uid);
2789 g_object_unref (mime_part);
2801 GnomeVFSResult result;
2803 ModestMsgViewWindow *window;
2806 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2807 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2808 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2809 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2812 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2816 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2817 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2818 g_free (pair->filename);
2819 g_object_unref (pair->part);
2820 g_slice_free (SaveMimePartPair, pair);
2822 g_list_free (info->pairs);
2825 g_object_unref (info->window);
2826 info->window = NULL;
2828 g_slice_free (SaveMimePartInfo, info);
2833 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2835 /* This is a GDK lock because we are an idle callback and
2836 * hildon_banner_show_information is or does Gtk+ code */
2838 gdk_threads_enter (); /* CHECKED */
2839 if (info->result == GNOME_VFS_OK) {
2840 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2841 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2844 /* Check if the uri belongs to the external mmc */
2845 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2846 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2848 msg = g_strdup (_KR("cerm_memory_card_full"));
2849 modest_platform_information_banner (NULL, NULL, msg);
2852 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2854 save_mime_part_info_free (info, FALSE);
2855 gdk_threads_leave (); /* CHECKED */
2861 save_mime_part_to_file (SaveMimePartInfo *info)
2863 GnomeVFSHandle *handle;
2865 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2867 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2868 if (info->result == GNOME_VFS_OK) {
2869 GError *error = NULL;
2870 gboolean decode_in_provider;
2872 ModestAccountMgr *mgr;
2873 const gchar *account;
2874 ModestProtocol *protocol = NULL;
2876 stream = tny_vfs_stream_new (handle);
2878 decode_in_provider = FALSE;
2879 mgr = modest_runtime_get_account_mgr ();
2880 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2881 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2882 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2883 decode_in_provider =
2884 modest_account_protocol_decode_part_to_stream (
2885 MODEST_ACCOUNT_PROTOCOL (protocol),
2893 if (!decode_in_provider)
2894 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2897 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2899 if ((error->domain == TNY_ERROR_DOMAIN) &&
2900 (error->code == TNY_IO_ERROR_WRITE) &&
2901 (errno == ENOSPC)) {
2902 info->result = GNOME_VFS_ERROR_NO_SPACE;
2904 info->result = GNOME_VFS_ERROR_IO;
2907 g_object_unref (G_OBJECT (stream));
2909 g_warning ("Could not create save attachment %s: %s\n",
2910 pair->filename, gnome_vfs_result_to_string (info->result));
2913 /* Go on saving remaining files */
2914 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2915 if (info->pairs != NULL) {
2916 save_mime_part_to_file (info);
2918 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2925 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2926 SaveMimePartInfo *info)
2928 gboolean is_ok = TRUE;
2929 gint replaced_files = 0;
2930 const GList *files = info->pairs;
2931 const GList *iter, *to_replace = NULL;
2933 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2934 SaveMimePartPair *pair = iter->data;
2935 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
2937 if (modest_utils_file_exists (unescaped)) {
2939 if (replaced_files == 1)
2944 if (replaced_files) {
2947 if (replaced_files == 1) {
2948 SaveMimePartPair *pair = to_replace->data;
2949 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2950 gchar *escaped_basename, *message;
2952 escaped_basename = g_uri_unescape_string (basename, NULL);
2953 message = g_strdup_printf ("%s\n%s",
2954 _FM("docm_nc_replace_file"),
2955 (escaped_basename) ? escaped_basename : "");
2956 response = modest_platform_run_confirmation_dialog (parent, message);
2958 g_free (escaped_basename);
2960 response = modest_platform_run_confirmation_dialog (parent,
2961 _FM("docm_nc_replace_multiple"));
2963 if (response != GTK_RESPONSE_OK)
2968 save_mime_part_info_free (info, TRUE);
2970 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2975 typedef struct _SaveAttachmentsInfo {
2976 TnyList *attachments_list;
2977 ModestMsgViewWindow *window;
2978 } SaveAttachmentsInfo;
2981 save_attachments_response (GtkDialog *dialog,
2985 TnyList *mime_parts;
2987 GList *files_to_save = NULL;
2988 gchar *current_folder;
2989 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
2991 mime_parts = TNY_LIST (sa_info->attachments_list);
2993 if (arg1 != GTK_RESPONSE_OK)
2996 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2997 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2998 if (current_folder && *current_folder != '\0') {
3000 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3001 current_folder,&err);
3003 g_debug ("Error storing latest used folder: %s", err->message);
3007 g_free (current_folder);
3009 if (!modest_utils_folder_writable (chooser_uri)) {
3010 const gchar *err_msg;
3012 #ifdef MODEST_PLATFORM_MAEMO
3013 if (modest_maemo_utils_in_usb_mode ()) {
3014 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3016 err_msg = _FM("sfil_ib_readonly_location");
3019 err_msg = _FM("sfil_ib_readonly_location");
3021 hildon_banner_show_information (NULL, NULL, err_msg);
3025 iter = tny_list_create_iterator (mime_parts);
3026 while (!tny_iterator_is_done (iter)) {
3027 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3029 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3030 !tny_mime_part_is_purged (mime_part) &&
3031 (tny_mime_part_get_filename (mime_part) != NULL)) {
3032 SaveMimePartPair *pair;
3034 pair = g_slice_new0 (SaveMimePartPair);
3036 if (tny_list_get_length (mime_parts) > 1) {
3038 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3039 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3042 pair->filename = g_strdup (chooser_uri);
3044 pair->part = mime_part;
3045 files_to_save = g_list_prepend (files_to_save, pair);
3047 tny_iterator_next (iter);
3049 g_object_unref (iter);
3052 if (files_to_save != NULL) {
3053 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3054 info->pairs = files_to_save;
3055 info->result = TRUE;
3056 info->uri = g_strdup (chooser_uri);
3057 info->window = g_object_ref (sa_info->window);
3058 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3060 g_free (chooser_uri);
3063 /* Free and close the dialog */
3064 g_object_unref (mime_parts);
3065 g_object_unref (sa_info->window);
3066 g_slice_free (SaveAttachmentsInfo, sa_info);
3067 gtk_widget_destroy (GTK_WIDGET (dialog));
3071 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3072 TnyList *mime_parts)
3074 ModestMsgViewWindowPrivate *priv;
3075 GtkWidget *save_dialog = NULL;
3076 gchar *conf_folder = NULL;
3077 gchar *filename = NULL;
3078 gchar *save_multiple_str = NULL;
3079 const gchar *root_folder = "file:///";
3081 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3082 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3084 if (mime_parts == NULL) {
3085 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3086 * selection available */
3087 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3088 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3089 g_object_unref (mime_parts);
3092 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3094 g_object_unref (mime_parts);
3100 g_object_ref (mime_parts);
3103 /* prepare dialog */
3104 if (tny_list_get_length (mime_parts) == 1) {
3106 /* only one attachment selected */
3107 iter = tny_list_create_iterator (mime_parts);
3108 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3109 g_object_unref (iter);
3110 if (!modest_tny_mime_part_is_msg (mime_part) &&
3111 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3112 !tny_mime_part_is_purged (mime_part)) {
3113 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3115 /* TODO: show any error? */
3116 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3117 g_object_unref (mime_parts);
3120 g_object_unref (mime_part);
3122 gint num = tny_list_get_length (mime_parts);
3123 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3124 "sfil_va_number_of_objects_attachment",
3125 "sfil_va_number_of_objects_attachments",
3129 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3130 GTK_FILE_CHOOSER_ACTION_SAVE);
3132 /* Get last used folder */
3133 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3134 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3136 /* File chooser stops working if we select "file:///" as current folder */
3137 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3138 g_free (conf_folder);
3142 if (conf_folder && conf_folder[0] != '\0') {
3143 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3146 /* Set the default folder to documents folder */
3147 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3150 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3152 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3153 g_free (docs_folder);
3155 g_free (conf_folder);
3159 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3164 /* if multiple, set multiple string */
3165 if (save_multiple_str) {
3166 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3167 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3168 g_free (save_multiple_str);
3171 /* We must run this asynchronously, because the hildon dialog
3172 performs a gtk_dialog_run by itself which leads to gdk
3174 SaveAttachmentsInfo *sa_info;
3175 sa_info = g_slice_new (SaveAttachmentsInfo);
3176 sa_info->attachments_list = mime_parts;
3177 sa_info->window = g_object_ref (window);
3178 g_signal_connect (save_dialog, "response",
3179 G_CALLBACK (save_attachments_response), sa_info);
3181 gtk_widget_show_all (save_dialog);
3185 show_remove_attachment_information (gpointer userdata)
3187 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3188 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3190 /* We're outside the main lock */
3191 gdk_threads_enter ();
3193 if (priv->remove_attachment_banner != NULL) {
3194 gtk_widget_destroy (priv->remove_attachment_banner);
3195 g_object_unref (priv->remove_attachment_banner);
3198 priv->remove_attachment_banner = g_object_ref (
3199 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3201 gdk_threads_leave ();
3207 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3209 ModestMsgViewWindowPrivate *priv;
3210 TnyList *mime_parts = NULL, *tmp;
3211 gchar *confirmation_message;
3217 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3218 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3220 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3221 * because we don't have selection
3223 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3225 /* Remove already purged messages from mime parts list. We use
3226 a copy of the list to remove items in the original one */
3227 tmp = tny_list_copy (mime_parts);
3228 iter = tny_list_create_iterator (tmp);
3229 while (!tny_iterator_is_done (iter)) {
3230 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3231 if (tny_mime_part_is_purged (part))
3232 tny_list_remove (mime_parts, (GObject *) part);
3234 g_object_unref (part);
3235 tny_iterator_next (iter);
3237 g_object_unref (tmp);
3238 g_object_unref (iter);
3240 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3241 tny_list_get_length (mime_parts) == 0) {
3242 g_object_unref (mime_parts);
3246 n_attachments = tny_list_get_length (mime_parts);
3247 if (n_attachments == 1) {
3251 iter = tny_list_create_iterator (mime_parts);
3252 part = (TnyMimePart *) tny_iterator_get_current (iter);
3253 g_object_unref (iter);
3254 if (modest_tny_mime_part_is_msg (part)) {
3256 header = tny_msg_get_header (TNY_MSG (part));
3257 filename = tny_header_dup_subject (header);
3258 g_object_unref (header);
3259 if (filename == NULL)
3260 filename = g_strdup (_("mail_va_no_subject"));
3262 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3264 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3266 g_object_unref (part);
3268 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3269 "mcen_nc_purge_files_text",
3270 n_attachments), n_attachments);
3272 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3273 confirmation_message);
3274 g_free (confirmation_message);
3276 if (response != GTK_RESPONSE_OK) {
3277 g_object_unref (mime_parts);
3281 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3283 iter = tny_list_create_iterator (mime_parts);
3284 while (!tny_iterator_is_done (iter)) {
3287 part = (TnyMimePart *) tny_iterator_get_current (iter);
3288 tny_mime_part_set_purged (TNY_MIME_PART (part));
3289 g_object_unref (part);
3290 tny_iterator_next (iter);
3292 g_object_unref (iter);
3294 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3295 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3296 tny_msg_rewrite_cache (msg);
3297 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3298 g_object_unref (msg);
3299 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3301 g_object_unref (mime_parts);
3303 if (priv->purge_timeout > 0) {
3304 g_source_remove (priv->purge_timeout);
3305 priv->purge_timeout = 0;
3308 if (priv->remove_attachment_banner) {
3309 gtk_widget_destroy (priv->remove_attachment_banner);
3310 g_object_unref (priv->remove_attachment_banner);
3311 priv->remove_attachment_banner = NULL;
3317 update_window_title (ModestMsgViewWindow *window)
3319 ModestMsgViewWindowPrivate *priv;
3321 TnyHeader *header = NULL;
3322 gchar *subject = NULL;
3324 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3326 /* Note that if the window is closed while we're retrieving
3327 the message, this widget could de deleted */
3328 if (!priv->msg_view)
3331 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3333 if (priv->other_body) {
3336 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3338 g_strstrip (description);
3339 subject = description;
3341 } else if (msg != NULL) {
3342 header = tny_msg_get_header (msg);
3343 subject = tny_header_dup_subject (header);
3344 g_object_unref (header);
3345 g_object_unref (msg);
3348 if ((subject == NULL)||(subject[0] == '\0')) {
3350 subject = g_strdup (_("mail_va_no_subject"));
3353 gtk_window_set_title (GTK_WINDOW (window), subject);
3358 on_move_focus (GtkWidget *widget,
3359 GtkDirectionType direction,
3362 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3366 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3368 GnomeVFSResult result;
3369 GnomeVFSHandle *handle = NULL;
3370 GnomeVFSFileInfo *info = NULL;
3373 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3374 if (result != GNOME_VFS_OK) {
3379 info = gnome_vfs_file_info_new ();
3380 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3381 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3382 /* We put a "safe" default size for going to cache */
3383 *expected_size = (300*1024);
3385 *expected_size = info->size;
3387 gnome_vfs_file_info_unref (info);
3389 stream = tny_vfs_stream_new (handle);
3398 TnyStream *output_stream;
3399 GtkWidget *msg_view;
3404 on_fetch_image_idle_refresh_view (gpointer userdata)
3407 FetchImageData *fidata = (FetchImageData *) userdata;
3409 gdk_threads_enter ();
3410 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3411 ModestMsgViewWindowPrivate *priv;
3413 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3414 priv->fetching_images--;
3415 gtk_widget_queue_draw (fidata->msg_view);
3416 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3418 gdk_threads_leave ();
3420 g_object_unref (fidata->msg_view);
3421 g_object_unref (fidata->window);
3422 g_slice_free (FetchImageData, fidata);
3427 on_fetch_image_thread (gpointer userdata)
3429 FetchImageData *fidata = (FetchImageData *) userdata;
3430 TnyStreamCache *cache;
3431 TnyStream *cache_stream;
3433 cache = modest_runtime_get_images_cache ();
3435 tny_stream_cache_get_stream (cache,
3437 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3438 (gpointer) fidata->uri);
3439 g_free (fidata->cache_id);
3440 g_free (fidata->uri);
3442 if (cache_stream != NULL) {
3445 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3448 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3449 if (G_UNLIKELY (nb_read < 0)) {
3451 } else if (G_LIKELY (nb_read > 0)) {
3452 gssize nb_written = 0;
3454 while (G_UNLIKELY (nb_written < nb_read)) {
3457 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3458 nb_read - nb_written);
3459 if (G_UNLIKELY (len < 0))
3465 tny_stream_close (cache_stream);
3466 g_object_unref (cache_stream);
3469 tny_stream_close (fidata->output_stream);
3470 g_object_unref (fidata->output_stream);
3472 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3478 on_fetch_image (ModestMsgView *msgview,
3481 ModestMsgViewWindow *window)
3483 const gchar *current_account;
3484 ModestMsgViewWindowPrivate *priv;
3485 FetchImageData *fidata;
3487 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3489 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3491 fidata = g_slice_new0 (FetchImageData);
3492 fidata->msg_view = g_object_ref (msgview);
3493 fidata->window = g_object_ref (window);
3494 fidata->uri = g_strdup (uri);
3495 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3496 fidata->output_stream = g_object_ref (stream);
3498 priv->fetching_images++;
3499 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3500 g_object_unref (fidata->output_stream);
3501 g_free (fidata->cache_id);
3502 g_free (fidata->uri);
3503 g_object_unref (fidata->msg_view);
3504 g_slice_free (FetchImageData, fidata);
3505 tny_stream_close (stream);
3506 priv->fetching_images--;
3507 update_progress_hint (window);
3510 update_progress_hint (window);
3516 setup_menu (ModestMsgViewWindow *self)
3518 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3520 /* Settings menu buttons */
3521 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3522 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3523 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3525 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3526 dngettext(GETTEXT_PACKAGE,
3527 "mcen_me_move_message",
3528 "mcen_me_move_messages",
3531 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3532 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3534 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3535 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3536 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3538 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3539 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3540 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3542 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3543 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3544 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3545 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3546 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3547 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3549 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3550 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3551 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3552 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3553 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3554 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3556 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3557 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3558 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3562 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3564 ModestMsgViewWindowPrivate *priv;
3565 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3566 GSList *recipients = NULL;
3568 gboolean contacts_to_add = FALSE;
3570 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3574 header = modest_msg_view_window_get_header (self);
3577 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3578 g_object_unref (header);
3580 recipients = modest_tny_msg_get_all_recipients_list (msg);
3581 g_object_unref (msg);
3584 if (recipients != NULL) {
3585 GtkWidget *picker_dialog;
3586 GtkWidget *selector;
3588 gchar *selected = NULL;
3590 selector = hildon_touch_selector_new_text ();
3591 g_object_ref (selector);
3593 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3594 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3595 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3596 (const gchar *) node->data);
3597 contacts_to_add = TRUE;
3601 if (contacts_to_add) {
3604 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3605 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3607 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3608 HILDON_TOUCH_SELECTOR (selector));
3610 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3612 if (picker_result == GTK_RESPONSE_OK) {
3613 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3615 gtk_widget_destroy (picker_dialog);
3618 modest_address_book_add_address (selected, (GtkWindow *) self);
3623 g_object_unref (selector);
3628 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3632 _modest_msg_view_window_map_event (GtkWidget *widget,
3636 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3638 update_progress_hint (self);
3644 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3646 ModestMsgViewWindowPrivate *priv;
3647 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3649 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3653 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3655 ModestMsgViewWindowPrivate *priv;
3656 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3658 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3660 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3664 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3666 ModestMsgViewWindowPrivate *priv;
3667 const gchar *msg_uid;
3668 TnyHeader *header = NULL;
3669 TnyFolder *folder = NULL;
3671 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3673 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3675 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3679 folder = tny_header_get_folder (header);
3680 g_object_unref (header);
3685 msg_uid = modest_msg_view_window_get_message_uid (self);
3687 GtkTreeRowReference *row_reference;
3689 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3690 row_reference = priv->row_reference;
3692 row_reference = NULL;
3694 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3695 g_warning ("Shouldn't happen, trying to reload a message failed");
3698 g_object_unref (folder);
3702 update_branding (ModestMsgViewWindow *self)
3704 const gchar *account;
3705 const gchar *mailbox;
3706 ModestAccountMgr *mgr;
3707 ModestProtocol *protocol = NULL;
3708 gchar *service_name = NULL;
3709 const GdkPixbuf *service_icon = NULL;
3710 ModestMsgViewWindowPrivate *priv;
3712 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3714 account = modest_window_get_active_account (MODEST_WINDOW (self));
3715 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3717 mgr = modest_runtime_get_account_mgr ();
3719 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3720 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3721 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3723 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3724 account, mailbox, MODEST_ICON_SIZE_SMALL);
3728 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3729 g_free (service_name);