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 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2160 g_object_unref (self);
2165 /* If there was any error */
2166 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2168 gtk_tree_row_reference_free (row_reference);
2169 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2171 /* Restore window title */
2172 update_window_title (self);
2173 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (self));
2174 g_object_unref (self);
2179 /* Get the window */
2180 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2181 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2182 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2184 /* Update the row reference */
2185 if (priv->row_reference != NULL) {
2186 gtk_tree_row_reference_free (priv->row_reference);
2187 priv->row_reference = (row_reference && gtk_tree_row_reference_valid (row_reference))?gtk_tree_row_reference_copy (row_reference):NULL;
2188 if (priv->next_row_reference != NULL) {
2189 gtk_tree_row_reference_free (priv->next_row_reference);
2191 if (priv->row_reference) {
2192 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2193 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2195 priv->next_row_reference = NULL;
2199 /* Mark header as read */
2200 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2201 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2203 /* Set new message */
2204 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2205 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2206 modest_msg_view_window_update_priority (self);
2207 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2208 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2209 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2212 /* Set the new message uid of the window */
2213 if (priv->msg_uid) {
2214 g_free (priv->msg_uid);
2215 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2218 /* Notify the observers */
2219 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2220 0, priv->header_model, priv->row_reference);
2223 g_object_unref (self);
2225 gtk_tree_row_reference_free (row_reference);
2229 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2231 ModestMsgViewWindowPrivate *priv;
2233 TnyFolderType folder_type;
2235 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2237 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2239 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2243 folder = tny_msg_get_folder (msg);
2245 folder_type = modest_tny_folder_guess_folder_type (folder);
2246 g_object_unref (folder);
2248 g_object_unref (msg);
2256 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2258 ModestMsgViewWindowPrivate *priv;
2259 TnyHeader *header = NULL;
2260 TnyHeaderFlags flags = 0;
2262 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2264 if (priv->header_model && priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
2266 GtkTreePath *path = NULL;
2268 path = gtk_tree_row_reference_get_path (priv->row_reference);
2269 g_return_if_fail (path != NULL);
2270 gtk_tree_model_get_iter (priv->header_model,
2272 gtk_tree_row_reference_get_path (priv->row_reference));
2274 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2276 gtk_tree_path_free (path);
2279 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2281 header = tny_msg_get_header (msg);
2282 g_object_unref (msg);
2287 flags = tny_header_get_flags (header);
2288 g_object_unref(G_OBJECT(header));
2291 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2296 toolbar_resize (ModestMsgViewWindow *self)
2298 ModestMsgViewWindowPrivate *priv = NULL;
2299 ModestWindowPrivate *parent_priv = NULL;
2301 gint static_button_size;
2302 ModestWindowMgr *mgr;
2304 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2305 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2306 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2308 mgr = modest_runtime_get_window_mgr ();
2309 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2311 if (parent_priv->toolbar) {
2312 /* Set expandable and homogeneous tool buttons */
2313 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2314 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2315 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2316 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2317 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2318 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2319 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageForward");
2320 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2321 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2322 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2323 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2324 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2325 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2326 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
2327 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
2328 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2329 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2330 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2331 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2336 modest_msg_view_window_show_toolbar (ModestWindow *self,
2337 gboolean show_toolbar)
2339 ModestMsgViewWindowPrivate *priv = NULL;
2340 ModestWindowPrivate *parent_priv;
2342 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2343 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2345 /* Set optimized view status */
2346 priv->optimized_view = !show_toolbar;
2348 if (!parent_priv->toolbar) {
2349 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2351 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2352 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2354 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2355 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2356 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2359 hildon_window_add_toolbar (HILDON_WINDOW (self),
2360 GTK_TOOLBAR (parent_priv->toolbar));
2365 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2366 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2367 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2369 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2370 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2371 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2373 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2376 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2377 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2382 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2384 ModestMsgViewWindow *window)
2386 if (!GTK_WIDGET_VISIBLE (window))
2389 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2393 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2395 ModestMsgViewWindowPrivate *priv;
2397 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2398 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2400 return priv->progress_hint;
2404 observers_empty (ModestMsgViewWindow *self)
2407 ModestMsgViewWindowPrivate *priv;
2408 gboolean is_empty = TRUE;
2409 guint pending_ops = 0;
2411 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2412 tmp = priv->progress_widgets;
2414 /* Check all observers */
2415 while (tmp && is_empty) {
2416 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2417 is_empty = pending_ops == 0;
2419 tmp = g_slist_next(tmp);
2426 on_account_removed (TnyAccountStore *account_store,
2427 TnyAccount *account,
2430 /* Do nothing if it's a transport account, because we only
2431 show the messages of a store account */
2432 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2433 const gchar *parent_acc = NULL;
2434 const gchar *our_acc = NULL;
2436 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2437 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2439 /* Close this window if I'm showing a message of the removed account */
2440 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2441 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2446 on_mail_operation_started (ModestMailOperation *mail_op,
2449 ModestMsgViewWindow *self;
2450 ModestMailOperationTypeOperation op_type;
2452 ModestMsgViewWindowPrivate *priv;
2453 GObject *source = NULL;
2455 self = MODEST_MSG_VIEW_WINDOW (user_data);
2456 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2457 op_type = modest_mail_operation_get_type_operation (mail_op);
2458 tmp = priv->progress_widgets;
2459 source = modest_mail_operation_get_source(mail_op);
2460 if (G_OBJECT (self) == source) {
2461 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2462 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2463 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2464 set_progress_hint (self, TRUE);
2466 modest_progress_object_add_operation (
2467 MODEST_PROGRESS_OBJECT (tmp->data),
2469 tmp = g_slist_next (tmp);
2473 g_object_unref (source);
2475 /* Update dimming rules */
2476 check_dimming_rules_after_change (self);
2480 on_mail_operation_finished (ModestMailOperation *mail_op,
2483 ModestMsgViewWindow *self;
2484 ModestMailOperationTypeOperation op_type;
2486 ModestMsgViewWindowPrivate *priv;
2488 self = MODEST_MSG_VIEW_WINDOW (user_data);
2489 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2490 op_type = modest_mail_operation_get_type_operation (mail_op);
2491 tmp = priv->progress_widgets;
2493 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2494 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2495 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2497 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2499 tmp = g_slist_next (tmp);
2502 /* If no more operations are being observed, NORMAL mode is enabled again */
2503 if (observers_empty (self)) {
2504 set_progress_hint (self, FALSE);
2508 /* Update dimming rules. We have to do this right here
2509 and not in view_msg_cb because at that point the
2510 transfer mode is still enabled so the dimming rule
2511 won't let the user delete the message that has been
2512 readed for example */
2513 check_dimming_rules_after_change (self);
2517 on_queue_changed (ModestMailOperationQueue *queue,
2518 ModestMailOperation *mail_op,
2519 ModestMailOperationQueueNotification type,
2520 ModestMsgViewWindow *self)
2522 ModestMsgViewWindowPrivate *priv;
2524 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2526 /* If this operations was created by another window, do nothing */
2527 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2530 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2531 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2533 "operation-started",
2534 G_CALLBACK (on_mail_operation_started),
2536 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2538 "operation-finished",
2539 G_CALLBACK (on_mail_operation_finished),
2541 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2542 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2544 "operation-started");
2545 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2547 "operation-finished");
2552 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2554 ModestMsgViewWindowPrivate *priv;
2555 TnyList *selected_attachments = NULL;
2557 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2558 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2560 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2561 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2563 return selected_attachments;
2567 ModestMsgViewWindow *self;
2569 } DecodeAsyncHelper;
2572 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2578 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2580 /* It could happen that the window was closed */
2581 if (GTK_WIDGET_VISIBLE (helper->self))
2582 set_progress_hint (helper->self, FALSE);
2584 if (cancelled || err) {
2586 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2587 modest_platform_information_banner (NULL, NULL, msg);
2593 /* make the file read-only */
2594 g_chmod(helper->file_path, 0444);
2596 /* Activate the file */
2597 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2601 g_object_unref (helper->self);
2602 g_free (helper->file_path);
2603 g_slice_free (DecodeAsyncHelper, helper);
2607 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2608 TnyMimePart *mime_part)
2610 ModestMsgViewWindowPrivate *priv;
2611 const gchar *msg_uid;
2612 gchar *attachment_uid = NULL;
2613 gint attachment_index = 0;
2614 TnyList *attachments;
2616 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2617 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2618 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2620 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2621 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2622 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2623 g_object_unref (attachments);
2625 if (msg_uid && attachment_index >= 0) {
2626 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2629 if (mime_part == NULL) {
2630 gboolean error = FALSE;
2631 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2632 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2634 } else if (tny_list_get_length (selected_attachments) > 1) {
2635 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2639 iter = tny_list_create_iterator (selected_attachments);
2640 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2641 g_object_unref (iter);
2643 if (selected_attachments)
2644 g_object_unref (selected_attachments);
2649 g_object_ref (mime_part);
2652 if (tny_mime_part_is_purged (mime_part))
2655 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2656 gchar *filepath = NULL;
2657 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2658 gboolean show_error_banner = FALSE;
2659 TnyFsStream *temp_stream = NULL;
2660 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2663 if (temp_stream != NULL) {
2664 ModestAccountMgr *mgr;
2665 DecodeAsyncHelper *helper;
2666 gboolean decode_in_provider;
2667 ModestProtocol *protocol;
2668 const gchar *account;
2670 /* Activate progress hint */
2671 set_progress_hint (window, TRUE);
2673 helper = g_slice_new0 (DecodeAsyncHelper);
2674 helper->self = g_object_ref (window);
2675 helper->file_path = g_strdup (filepath);
2677 decode_in_provider = FALSE;
2678 mgr = modest_runtime_get_account_mgr ();
2679 account = modest_window_get_active_account (MODEST_WINDOW (window));
2680 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2681 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2682 decode_in_provider =
2683 modest_account_protocol_decode_part_to_stream_async (
2684 MODEST_ACCOUNT_PROTOCOL (protocol),
2687 TNY_STREAM (temp_stream),
2688 on_decode_to_stream_async_handler,
2694 if (!decode_in_provider)
2695 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2696 on_decode_to_stream_async_handler,
2699 g_object_unref (temp_stream);
2700 /* NOTE: files in the temporary area will be automatically
2701 * cleaned after some time if they are no longer in use */
2704 const gchar *content_type;
2705 /* the file may already exist but it isn't writable,
2706 * let's try to open it anyway */
2707 content_type = tny_mime_part_get_content_type (mime_part);
2708 modest_platform_activate_file (filepath, content_type);
2710 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2711 show_error_banner = TRUE;
2716 if (show_error_banner)
2717 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2718 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2719 ModestWindowMgr *mgr;
2720 ModestWindow *msg_win = NULL;
2721 TnyMsg *current_msg;
2725 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2726 mgr = modest_runtime_get_window_mgr ();
2727 header = tny_msg_get_header (TNY_MSG (current_msg));
2728 found = modest_window_mgr_find_registered_message_uid (mgr,
2733 g_debug ("window for this body is already being created");
2736 /* it's not found, so create a new window for it */
2737 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2738 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2739 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2741 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2743 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2744 account, mailbox, attachment_uid);
2746 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2747 modest_window_get_zoom (MODEST_WINDOW (window)));
2748 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2749 gtk_widget_show_all (GTK_WIDGET (msg_win));
2751 gtk_widget_destroy (GTK_WIDGET (msg_win));
2753 g_object_unref (current_msg);
2755 /* message attachment */
2756 TnyHeader *header = NULL;
2757 ModestWindowMgr *mgr;
2758 ModestWindow *msg_win = NULL;
2761 header = tny_msg_get_header (TNY_MSG (mime_part));
2762 mgr = modest_runtime_get_window_mgr ();
2763 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2766 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2767 * thus, we don't do anything */
2768 g_debug ("window for is already being created");
2770 /* it's not found, so create a new window for it */
2771 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2772 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2773 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2775 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2776 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2777 mailbox, attachment_uid);
2778 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2779 modest_window_get_zoom (MODEST_WINDOW (window)));
2780 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2781 gtk_widget_show_all (GTK_WIDGET (msg_win));
2783 gtk_widget_destroy (GTK_WIDGET (msg_win));
2789 g_free (attachment_uid);
2791 g_object_unref (mime_part);
2803 GnomeVFSResult result;
2805 ModestMsgViewWindow *window;
2808 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2809 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2810 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2811 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2814 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2818 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2819 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2820 g_free (pair->filename);
2821 g_object_unref (pair->part);
2822 g_slice_free (SaveMimePartPair, pair);
2824 g_list_free (info->pairs);
2827 g_object_unref (info->window);
2828 info->window = NULL;
2830 g_slice_free (SaveMimePartInfo, info);
2835 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2837 /* This is a GDK lock because we are an idle callback and
2838 * hildon_banner_show_information is or does Gtk+ code */
2840 gdk_threads_enter (); /* CHECKED */
2841 if (info->result == GNOME_VFS_OK) {
2842 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2843 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2846 /* Check if the uri belongs to the external mmc */
2847 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2848 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2850 msg = g_strdup (_KR("cerm_memory_card_full"));
2851 modest_platform_information_banner (NULL, NULL, msg);
2854 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2856 save_mime_part_info_free (info, FALSE);
2857 gdk_threads_leave (); /* CHECKED */
2863 save_mime_part_to_file (SaveMimePartInfo *info)
2865 GnomeVFSHandle *handle;
2867 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2869 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2870 if (info->result == GNOME_VFS_OK) {
2871 GError *error = NULL;
2872 gboolean decode_in_provider;
2874 ModestAccountMgr *mgr;
2875 const gchar *account;
2876 ModestProtocol *protocol = NULL;
2878 stream = tny_vfs_stream_new (handle);
2880 decode_in_provider = FALSE;
2881 mgr = modest_runtime_get_account_mgr ();
2882 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2883 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2884 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2885 decode_in_provider =
2886 modest_account_protocol_decode_part_to_stream (
2887 MODEST_ACCOUNT_PROTOCOL (protocol),
2895 if (!decode_in_provider)
2896 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2899 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2901 if ((error->domain == TNY_ERROR_DOMAIN) &&
2902 (error->code == TNY_IO_ERROR_WRITE) &&
2903 (errno == ENOSPC)) {
2904 info->result = GNOME_VFS_ERROR_NO_SPACE;
2906 info->result = GNOME_VFS_ERROR_IO;
2909 g_object_unref (G_OBJECT (stream));
2911 g_warning ("Could not create save attachment %s: %s\n",
2912 pair->filename, gnome_vfs_result_to_string (info->result));
2915 /* Go on saving remaining files */
2916 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2917 if (info->pairs != NULL) {
2918 save_mime_part_to_file (info);
2920 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2927 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2928 SaveMimePartInfo *info)
2930 gboolean is_ok = TRUE;
2931 gint replaced_files = 0;
2932 const GList *files = info->pairs;
2933 const GList *iter, *to_replace = NULL;
2935 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2936 SaveMimePartPair *pair = iter->data;
2937 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
2939 if (modest_utils_file_exists (unescaped)) {
2941 if (replaced_files == 1)
2946 if (replaced_files) {
2949 if (replaced_files == 1) {
2950 SaveMimePartPair *pair = to_replace->data;
2951 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2952 gchar *escaped_basename, *message;
2954 escaped_basename = g_uri_unescape_string (basename, NULL);
2955 message = g_strdup_printf ("%s\n%s",
2956 _FM("docm_nc_replace_file"),
2957 (escaped_basename) ? escaped_basename : "");
2958 response = modest_platform_run_confirmation_dialog (parent, message);
2960 g_free (escaped_basename);
2962 response = modest_platform_run_confirmation_dialog (parent,
2963 _FM("docm_nc_replace_multiple"));
2965 if (response != GTK_RESPONSE_OK)
2970 save_mime_part_info_free (info, TRUE);
2972 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2977 typedef struct _SaveAttachmentsInfo {
2978 TnyList *attachments_list;
2979 ModestMsgViewWindow *window;
2980 } SaveAttachmentsInfo;
2983 save_attachments_response (GtkDialog *dialog,
2987 TnyList *mime_parts;
2989 GList *files_to_save = NULL;
2990 gchar *current_folder;
2991 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
2993 mime_parts = TNY_LIST (sa_info->attachments_list);
2995 if (arg1 != GTK_RESPONSE_OK)
2998 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2999 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3000 if (current_folder && *current_folder != '\0') {
3002 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3003 current_folder,&err);
3005 g_debug ("Error storing latest used folder: %s", err->message);
3009 g_free (current_folder);
3011 if (!modest_utils_folder_writable (chooser_uri)) {
3012 const gchar *err_msg;
3014 #ifdef MODEST_PLATFORM_MAEMO
3015 if (modest_maemo_utils_in_usb_mode ()) {
3016 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3018 err_msg = _FM("sfil_ib_readonly_location");
3021 err_msg = _FM("sfil_ib_readonly_location");
3023 hildon_banner_show_information (NULL, NULL, err_msg);
3027 iter = tny_list_create_iterator (mime_parts);
3028 while (!tny_iterator_is_done (iter)) {
3029 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3031 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3032 !tny_mime_part_is_purged (mime_part) &&
3033 (tny_mime_part_get_filename (mime_part) != NULL)) {
3034 SaveMimePartPair *pair;
3036 pair = g_slice_new0 (SaveMimePartPair);
3038 if (tny_list_get_length (mime_parts) > 1) {
3040 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3041 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3044 pair->filename = g_strdup (chooser_uri);
3046 pair->part = mime_part;
3047 files_to_save = g_list_prepend (files_to_save, pair);
3049 tny_iterator_next (iter);
3051 g_object_unref (iter);
3054 if (files_to_save != NULL) {
3055 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3056 info->pairs = files_to_save;
3057 info->result = TRUE;
3058 info->uri = g_strdup (chooser_uri);
3059 info->window = g_object_ref (sa_info->window);
3060 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3062 g_free (chooser_uri);
3065 /* Free and close the dialog */
3066 g_object_unref (mime_parts);
3067 g_object_unref (sa_info->window);
3068 g_slice_free (SaveAttachmentsInfo, sa_info);
3069 gtk_widget_destroy (GTK_WIDGET (dialog));
3073 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3074 TnyList *mime_parts)
3076 ModestMsgViewWindowPrivate *priv;
3077 GtkWidget *save_dialog = NULL;
3078 gchar *conf_folder = NULL;
3079 gchar *filename = NULL;
3080 gchar *save_multiple_str = NULL;
3081 const gchar *root_folder = "file:///";
3083 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3084 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3086 if (mime_parts == NULL) {
3087 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3088 * selection available */
3089 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3090 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3091 g_object_unref (mime_parts);
3094 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3096 g_object_unref (mime_parts);
3102 g_object_ref (mime_parts);
3105 /* prepare dialog */
3106 if (tny_list_get_length (mime_parts) == 1) {
3108 /* only one attachment selected */
3109 iter = tny_list_create_iterator (mime_parts);
3110 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3111 g_object_unref (iter);
3112 if (!modest_tny_mime_part_is_msg (mime_part) &&
3113 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3114 !tny_mime_part_is_purged (mime_part)) {
3115 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3117 /* TODO: show any error? */
3118 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3119 g_object_unref (mime_parts);
3122 g_object_unref (mime_part);
3124 gint num = tny_list_get_length (mime_parts);
3125 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3126 "sfil_va_number_of_objects_attachment",
3127 "sfil_va_number_of_objects_attachments",
3131 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3132 GTK_FILE_CHOOSER_ACTION_SAVE);
3134 /* Get last used folder */
3135 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3136 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3138 /* File chooser stops working if we select "file:///" as current folder */
3139 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3140 g_free (conf_folder);
3144 if (conf_folder && conf_folder[0] != '\0') {
3145 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3148 /* Set the default folder to documents folder */
3149 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3152 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3154 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3155 g_free (docs_folder);
3157 g_free (conf_folder);
3161 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3166 /* if multiple, set multiple string */
3167 if (save_multiple_str) {
3168 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3169 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3170 g_free (save_multiple_str);
3173 /* We must run this asynchronously, because the hildon dialog
3174 performs a gtk_dialog_run by itself which leads to gdk
3176 SaveAttachmentsInfo *sa_info;
3177 sa_info = g_slice_new (SaveAttachmentsInfo);
3178 sa_info->attachments_list = mime_parts;
3179 sa_info->window = g_object_ref (window);
3180 g_signal_connect (save_dialog, "response",
3181 G_CALLBACK (save_attachments_response), sa_info);
3183 gtk_widget_show_all (save_dialog);
3187 show_remove_attachment_information (gpointer userdata)
3189 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3190 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3192 /* We're outside the main lock */
3193 gdk_threads_enter ();
3195 if (priv->remove_attachment_banner != NULL) {
3196 gtk_widget_destroy (priv->remove_attachment_banner);
3197 g_object_unref (priv->remove_attachment_banner);
3200 priv->remove_attachment_banner = g_object_ref (
3201 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3203 gdk_threads_leave ();
3209 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3211 ModestMsgViewWindowPrivate *priv;
3212 TnyList *mime_parts = NULL, *tmp;
3213 gchar *confirmation_message;
3219 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3220 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3222 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3223 * because we don't have selection
3225 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3227 /* Remove already purged messages from mime parts list. We use
3228 a copy of the list to remove items in the original one */
3229 tmp = tny_list_copy (mime_parts);
3230 iter = tny_list_create_iterator (tmp);
3231 while (!tny_iterator_is_done (iter)) {
3232 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3233 if (tny_mime_part_is_purged (part))
3234 tny_list_remove (mime_parts, (GObject *) part);
3236 g_object_unref (part);
3237 tny_iterator_next (iter);
3239 g_object_unref (tmp);
3240 g_object_unref (iter);
3242 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3243 tny_list_get_length (mime_parts) == 0) {
3244 g_object_unref (mime_parts);
3248 n_attachments = tny_list_get_length (mime_parts);
3249 if (n_attachments == 1) {
3253 iter = tny_list_create_iterator (mime_parts);
3254 part = (TnyMimePart *) tny_iterator_get_current (iter);
3255 g_object_unref (iter);
3256 if (modest_tny_mime_part_is_msg (part)) {
3258 header = tny_msg_get_header (TNY_MSG (part));
3259 filename = tny_header_dup_subject (header);
3260 g_object_unref (header);
3261 if (filename == NULL)
3262 filename = g_strdup (_("mail_va_no_subject"));
3264 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3266 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3268 g_object_unref (part);
3270 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3271 "mcen_nc_purge_files_text",
3272 n_attachments), n_attachments);
3274 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3275 confirmation_message);
3276 g_free (confirmation_message);
3278 if (response != GTK_RESPONSE_OK) {
3279 g_object_unref (mime_parts);
3283 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3285 iter = tny_list_create_iterator (mime_parts);
3286 while (!tny_iterator_is_done (iter)) {
3289 part = (TnyMimePart *) tny_iterator_get_current (iter);
3290 tny_mime_part_set_purged (TNY_MIME_PART (part));
3291 g_object_unref (part);
3292 tny_iterator_next (iter);
3294 g_object_unref (iter);
3296 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3297 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3298 tny_msg_rewrite_cache (msg);
3299 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3300 g_object_unref (msg);
3301 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3303 g_object_unref (mime_parts);
3305 if (priv->purge_timeout > 0) {
3306 g_source_remove (priv->purge_timeout);
3307 priv->purge_timeout = 0;
3310 if (priv->remove_attachment_banner) {
3311 gtk_widget_destroy (priv->remove_attachment_banner);
3312 g_object_unref (priv->remove_attachment_banner);
3313 priv->remove_attachment_banner = NULL;
3319 update_window_title (ModestMsgViewWindow *window)
3321 ModestMsgViewWindowPrivate *priv;
3323 TnyHeader *header = NULL;
3324 gchar *subject = NULL;
3326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3328 /* Note that if the window is closed while we're retrieving
3329 the message, this widget could de deleted */
3330 if (!priv->msg_view)
3333 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3335 if (priv->other_body) {
3338 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3340 g_strstrip (description);
3341 subject = description;
3343 } else if (msg != NULL) {
3344 header = tny_msg_get_header (msg);
3345 subject = tny_header_dup_subject (header);
3346 g_object_unref (header);
3347 g_object_unref (msg);
3350 if ((subject == NULL)||(subject[0] == '\0')) {
3352 subject = g_strdup (_("mail_va_no_subject"));
3355 gtk_window_set_title (GTK_WINDOW (window), subject);
3360 on_move_focus (GtkWidget *widget,
3361 GtkDirectionType direction,
3364 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3368 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3370 GnomeVFSResult result;
3371 GnomeVFSHandle *handle = NULL;
3372 GnomeVFSFileInfo *info = NULL;
3375 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3376 if (result != GNOME_VFS_OK) {
3381 info = gnome_vfs_file_info_new ();
3382 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3383 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3384 /* We put a "safe" default size for going to cache */
3385 *expected_size = (300*1024);
3387 *expected_size = info->size;
3389 gnome_vfs_file_info_unref (info);
3391 stream = tny_vfs_stream_new (handle);
3400 TnyStream *output_stream;
3401 GtkWidget *msg_view;
3406 on_fetch_image_idle_refresh_view (gpointer userdata)
3409 FetchImageData *fidata = (FetchImageData *) userdata;
3411 gdk_threads_enter ();
3412 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3413 ModestMsgViewWindowPrivate *priv;
3415 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3416 priv->fetching_images--;
3417 gtk_widget_queue_draw (fidata->msg_view);
3418 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3420 gdk_threads_leave ();
3422 g_object_unref (fidata->msg_view);
3423 g_object_unref (fidata->window);
3424 g_slice_free (FetchImageData, fidata);
3429 on_fetch_image_thread (gpointer userdata)
3431 FetchImageData *fidata = (FetchImageData *) userdata;
3432 TnyStreamCache *cache;
3433 TnyStream *cache_stream;
3435 cache = modest_runtime_get_images_cache ();
3437 tny_stream_cache_get_stream (cache,
3439 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3440 (gpointer) fidata->uri);
3441 g_free (fidata->cache_id);
3442 g_free (fidata->uri);
3444 if (cache_stream != NULL) {
3447 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3450 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3451 if (G_UNLIKELY (nb_read < 0)) {
3453 } else if (G_LIKELY (nb_read > 0)) {
3454 gssize nb_written = 0;
3456 while (G_UNLIKELY (nb_written < nb_read)) {
3459 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3460 nb_read - nb_written);
3461 if (G_UNLIKELY (len < 0))
3467 tny_stream_close (cache_stream);
3468 g_object_unref (cache_stream);
3471 tny_stream_close (fidata->output_stream);
3472 g_object_unref (fidata->output_stream);
3474 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3480 on_fetch_image (ModestMsgView *msgview,
3483 ModestMsgViewWindow *window)
3485 const gchar *current_account;
3486 ModestMsgViewWindowPrivate *priv;
3487 FetchImageData *fidata;
3489 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3491 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3493 fidata = g_slice_new0 (FetchImageData);
3494 fidata->msg_view = g_object_ref (msgview);
3495 fidata->window = g_object_ref (window);
3496 fidata->uri = g_strdup (uri);
3497 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3498 fidata->output_stream = g_object_ref (stream);
3500 priv->fetching_images++;
3501 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3502 g_object_unref (fidata->output_stream);
3503 g_free (fidata->cache_id);
3504 g_free (fidata->uri);
3505 g_object_unref (fidata->msg_view);
3506 g_slice_free (FetchImageData, fidata);
3507 tny_stream_close (stream);
3508 priv->fetching_images--;
3509 update_progress_hint (window);
3512 update_progress_hint (window);
3518 setup_menu (ModestMsgViewWindow *self)
3520 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3522 /* Settings menu buttons */
3523 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3524 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3525 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3527 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3528 dngettext(GETTEXT_PACKAGE,
3529 "mcen_me_move_message",
3530 "mcen_me_move_messages",
3533 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3534 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3536 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3537 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3538 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3540 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3541 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3542 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3544 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3545 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3546 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3547 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3548 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3549 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3551 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3552 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3553 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3554 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3555 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3556 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3558 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3559 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3560 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3564 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3566 ModestMsgViewWindowPrivate *priv;
3567 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3568 GSList *recipients = NULL;
3570 gboolean contacts_to_add = FALSE;
3572 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3576 header = modest_msg_view_window_get_header (self);
3579 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3580 g_object_unref (header);
3582 recipients = modest_tny_msg_get_all_recipients_list (msg);
3583 g_object_unref (msg);
3586 if (recipients != NULL) {
3587 GtkWidget *picker_dialog;
3588 GtkWidget *selector;
3590 gchar *selected = NULL;
3592 selector = hildon_touch_selector_new_text ();
3593 g_object_ref (selector);
3595 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3596 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3597 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3598 (const gchar *) node->data);
3599 contacts_to_add = TRUE;
3603 if (contacts_to_add) {
3606 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3607 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3609 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3610 HILDON_TOUCH_SELECTOR (selector));
3612 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3614 if (picker_result == GTK_RESPONSE_OK) {
3615 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3617 gtk_widget_destroy (picker_dialog);
3620 modest_address_book_add_address (selected, (GtkWindow *) self);
3625 g_object_unref (selector);
3630 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3634 _modest_msg_view_window_map_event (GtkWidget *widget,
3638 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3640 update_progress_hint (self);
3646 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3648 ModestMsgViewWindowPrivate *priv;
3649 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3651 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3655 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3657 ModestMsgViewWindowPrivate *priv;
3658 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3660 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3662 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3666 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3668 ModestMsgViewWindowPrivate *priv;
3669 const gchar *msg_uid;
3670 TnyHeader *header = NULL;
3671 TnyFolder *folder = NULL;
3673 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3675 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3677 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3681 folder = tny_header_get_folder (header);
3682 g_object_unref (header);
3687 msg_uid = modest_msg_view_window_get_message_uid (self);
3689 GtkTreeRowReference *row_reference;
3691 if (priv->row_reference && gtk_tree_row_reference_valid (priv->row_reference)) {
3692 row_reference = priv->row_reference;
3694 row_reference = NULL;
3696 if (!message_reader (self, priv, NULL, msg_uid, folder, row_reference))
3697 g_warning ("Shouldn't happen, trying to reload a message failed");
3700 g_object_unref (folder);
3704 update_branding (ModestMsgViewWindow *self)
3706 const gchar *account;
3707 const gchar *mailbox;
3708 ModestAccountMgr *mgr;
3709 ModestProtocol *protocol = NULL;
3710 gchar *service_name = NULL;
3711 const GdkPixbuf *service_icon = NULL;
3712 ModestMsgViewWindowPrivate *priv;
3714 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3716 account = modest_window_get_active_account (MODEST_WINDOW (self));
3717 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3719 mgr = modest_runtime_get_account_mgr ();
3721 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3722 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3723 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3725 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3726 account, mailbox, MODEST_ICON_SIZE_SMALL);
3730 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3731 g_free (service_name);