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);
234 /* list my signals */
241 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
242 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
245 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
246 MODEST_TYPE_MSG_VIEW_WINDOW, \
247 ModestMsgViewWindowPrivate))
249 static GtkWindowClass *parent_class = NULL;
251 /* uncomment the following if you have defined any signals */
252 static guint signals[LAST_SIGNAL] = {0};
255 modest_msg_view_window_get_type (void)
257 static GType my_type = 0;
259 static const GTypeInfo my_info = {
260 sizeof(ModestMsgViewWindowClass),
261 NULL, /* base init */
262 NULL, /* base finalize */
263 (GClassInitFunc) modest_msg_view_window_class_init,
264 NULL, /* class finalize */
265 NULL, /* class data */
266 sizeof(ModestMsgViewWindow),
268 (GInstanceInitFunc) modest_msg_view_window_init,
271 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
272 "ModestMsgViewWindow",
275 static const GInterfaceInfo modest_header_view_observer_info =
277 (GInterfaceInitFunc) modest_header_view_observer_init,
278 NULL, /* interface_finalize */
279 NULL /* interface_data */
282 g_type_add_interface_static (my_type,
283 MODEST_TYPE_HEADER_VIEW_OBSERVER,
284 &modest_header_view_observer_info);
290 save_state (ModestWindow *self)
292 modest_widget_memory_save (modest_runtime_get_conf (),
294 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
298 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
299 GtkScrollType scroll_type,
303 ModestMsgViewWindowPrivate *priv;
304 gboolean return_value;
306 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
307 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
312 add_scroll_binding (GtkBindingSet *binding_set,
314 GtkScrollType scroll)
316 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
318 gtk_binding_entry_add_signal (binding_set, keyval, 0,
320 GTK_TYPE_SCROLL_TYPE, scroll,
321 G_TYPE_BOOLEAN, FALSE);
322 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
324 GTK_TYPE_SCROLL_TYPE, scroll,
325 G_TYPE_BOOLEAN, FALSE);
329 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
331 GObjectClass *gobject_class;
332 HildonWindowClass *hildon_window_class;
333 ModestWindowClass *modest_window_class;
334 GtkBindingSet *binding_set;
336 gobject_class = (GObjectClass*) klass;
337 hildon_window_class = (HildonWindowClass *) klass;
338 modest_window_class = (ModestWindowClass *) klass;
340 parent_class = g_type_class_peek_parent (klass);
341 gobject_class->finalize = modest_msg_view_window_finalize;
343 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
344 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
345 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
346 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
347 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
348 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
350 modest_window_class->save_state_func = save_state;
352 klass->scroll_child = modest_msg_view_window_scroll_child;
354 signals[MSG_CHANGED_SIGNAL] =
355 g_signal_new ("msg-changed",
356 G_TYPE_FROM_CLASS (gobject_class),
358 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
360 modest_marshal_VOID__POINTER_POINTER,
361 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
363 signals[SCROLL_CHILD_SIGNAL] =
364 g_signal_new ("scroll-child",
365 G_TYPE_FROM_CLASS (gobject_class),
366 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
367 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
369 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
370 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
372 binding_set = gtk_binding_set_by_class (klass);
373 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
374 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
375 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
376 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
377 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
378 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
380 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
384 static void modest_header_view_observer_init(
385 ModestHeaderViewObserverIface *iface_class)
387 iface_class->update_func = modest_msg_view_window_update_model_replaced;
391 modest_msg_view_window_init (ModestMsgViewWindow *obj)
393 ModestMsgViewWindowPrivate *priv;
394 ModestWindowPrivate *parent_priv = NULL;
395 GtkActionGroup *action_group = NULL;
396 GError *error = NULL;
398 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
399 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
400 parent_priv->ui_manager = gtk_ui_manager_new();
402 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
403 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
405 /* Add common actions */
406 gtk_action_group_add_actions (action_group,
407 modest_action_entries,
408 G_N_ELEMENTS (modest_action_entries),
410 gtk_action_group_add_toggle_actions (action_group,
411 msg_view_toggle_action_entries,
412 G_N_ELEMENTS (msg_view_toggle_action_entries),
415 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
416 g_object_unref (action_group);
418 /* Load the UI definition */
419 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
422 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
423 g_error_free (error);
428 /* Add accelerators */
429 gtk_window_add_accel_group (GTK_WINDOW (obj),
430 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
432 priv->is_search_result = FALSE;
433 priv->is_outbox = FALSE;
435 priv->msg_view = NULL;
436 priv->header_model = NULL;
437 priv->header_folder_id = NULL;
438 priv->clipboard_change_handler = 0;
439 priv->queue_change_handler = 0;
440 priv->account_removed_handler = 0;
441 priv->row_changed_handler = 0;
442 priv->row_deleted_handler = 0;
443 priv->row_inserted_handler = 0;
444 priv->rows_reordered_handler = 0;
445 priv->progress_hint = FALSE;
446 priv->fetching_images = 0;
448 priv->optimized_view = FALSE;
449 priv->purge_timeout = 0;
450 priv->remove_attachment_banner = NULL;
451 priv->msg_uid = NULL;
452 priv->other_body = NULL;
454 priv->sighandlers = NULL;
457 init_window (MODEST_MSG_VIEW_WINDOW(obj));
459 hildon_program_add_window (hildon_program_get_instance(),
465 update_progress_hint (ModestMsgViewWindow *self)
467 ModestMsgViewWindowPrivate *priv;
468 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
470 if (GTK_WIDGET_VISIBLE (self)) {
471 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
472 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
477 set_progress_hint (ModestMsgViewWindow *self,
480 ModestWindowPrivate *parent_priv;
481 ModestMsgViewWindowPrivate *priv;
483 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
485 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
486 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
488 /* Sets current progress hint */
489 priv->progress_hint = enabled;
491 update_progress_hint (self);
497 init_window (ModestMsgViewWindow *obj)
499 GtkWidget *main_vbox;
500 ModestMsgViewWindowPrivate *priv;
502 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
504 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
505 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
506 main_vbox = gtk_vbox_new (FALSE, 6);
507 priv->main_scroll = hildon_pannable_area_new ();
508 g_object_set (G_OBJECT (priv->main_scroll), "mov-mode", HILDON_MOVEMENT_MODE_BOTH, NULL);
509 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
510 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
511 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
513 /* NULL-ize fields if the window is destroyed */
514 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
516 gtk_widget_show_all (GTK_WIDGET(main_vbox));
520 modest_msg_view_window_disconnect_signals (ModestWindow *self)
522 ModestMsgViewWindowPrivate *priv;
523 GtkWidget *header_view = NULL;
524 GtkWindow *parent_window = NULL;
526 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
528 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
529 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
530 priv->clipboard_change_handler))
531 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
532 priv->clipboard_change_handler);
534 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
535 priv->queue_change_handler))
536 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
537 priv->queue_change_handler);
539 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
540 priv->account_removed_handler))
541 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
542 priv->account_removed_handler);
544 if (priv->header_model) {
545 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
546 priv->row_changed_handler))
547 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
548 priv->row_changed_handler);
550 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
551 priv->row_deleted_handler))
552 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
553 priv->row_deleted_handler);
555 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
556 priv->row_inserted_handler))
557 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
558 priv->row_inserted_handler);
560 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
561 priv->rows_reordered_handler))
562 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
563 priv->rows_reordered_handler);
566 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
567 priv->sighandlers = NULL;
569 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
570 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
571 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
573 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
574 MODEST_HEADER_VIEW_OBSERVER(self));
580 modest_msg_view_window_finalize (GObject *obj)
582 ModestMsgViewWindowPrivate *priv;
584 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
586 /* Sanity check: shouldn't be needed, the window mgr should
587 call this function before */
588 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
590 if (priv->other_body != NULL) {
591 g_object_unref (priv->other_body);
592 priv->other_body = NULL;
595 if (priv->header_model != NULL) {
596 g_object_unref (priv->header_model);
597 priv->header_model = NULL;
600 if (priv->remove_attachment_banner) {
601 gtk_widget_destroy (priv->remove_attachment_banner);
602 g_object_unref (priv->remove_attachment_banner);
603 priv->remove_attachment_banner = NULL;
606 if (priv->purge_timeout > 0) {
607 g_source_remove (priv->purge_timeout);
608 priv->purge_timeout = 0;
611 if (priv->row_reference) {
612 gtk_tree_row_reference_free (priv->row_reference);
613 priv->row_reference = NULL;
616 if (priv->next_row_reference) {
617 gtk_tree_row_reference_free (priv->next_row_reference);
618 priv->next_row_reference = NULL;
622 g_free (priv->msg_uid);
623 priv->msg_uid = NULL;
626 G_OBJECT_CLASS(parent_class)->finalize (obj);
630 select_next_valid_row (GtkTreeModel *model,
631 GtkTreeRowReference **row_reference,
635 GtkTreeIter tmp_iter;
637 GtkTreePath *next = NULL;
638 gboolean retval = FALSE, finished;
640 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
642 path = gtk_tree_row_reference_get_path (*row_reference);
643 gtk_tree_model_get_iter (model, &tmp_iter, path);
644 gtk_tree_row_reference_free (*row_reference);
645 *row_reference = NULL;
649 TnyHeader *header = NULL;
651 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
652 gtk_tree_model_get (model, &tmp_iter,
653 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
657 if (msg_is_visible (header, is_outbox)) {
658 next = gtk_tree_model_get_path (model, &tmp_iter);
659 *row_reference = gtk_tree_row_reference_new (model, next);
660 gtk_tree_path_free (next);
664 g_object_unref (header);
667 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
668 next = gtk_tree_model_get_path (model, &tmp_iter);
670 /* Ensure that we are not selecting the same */
671 if (gtk_tree_path_compare (path, next) != 0) {
672 gtk_tree_model_get (model, &tmp_iter,
673 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
676 if (msg_is_visible (header, is_outbox)) {
677 *row_reference = gtk_tree_row_reference_new (model, next);
681 g_object_unref (header);
685 /* If we ended up in the same message
686 then there is no valid next
690 gtk_tree_path_free (next);
692 /* If there are no more messages and we don't
693 want to start again in the first one then
694 there is no valid next message */
700 gtk_tree_path_free (path);
705 /* TODO: This should be in _init(), with the parameters as properties. */
707 modest_msg_view_window_construct (ModestMsgViewWindow *self,
708 const gchar *modest_account_name,
709 const gchar *mailbox,
710 const gchar *msg_uid)
713 ModestMsgViewWindowPrivate *priv = NULL;
714 ModestWindowPrivate *parent_priv = NULL;
715 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
716 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
718 obj = G_OBJECT (self);
719 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
720 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
722 priv->msg_uid = g_strdup (msg_uid);
725 parent_priv->menubar = NULL;
727 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
728 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
731 /* Add common dimming rules */
732 modest_dimming_rules_group_add_rules (toolbar_rules_group,
733 modest_msg_view_toolbar_dimming_entries,
734 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
735 MODEST_WINDOW (self));
736 modest_dimming_rules_group_add_rules (clipboard_rules_group,
737 modest_msg_view_clipboard_dimming_entries,
738 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
739 MODEST_WINDOW (self));
741 /* Insert dimming rules group for this window */
742 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
743 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
744 g_object_unref (toolbar_rules_group);
745 g_object_unref (clipboard_rules_group);
747 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
749 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);
750 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
751 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
752 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
753 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
754 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
755 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
756 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
757 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
758 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
759 G_CALLBACK (modest_ui_actions_on_details), obj);
760 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
761 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
762 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
763 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
764 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
765 G_CALLBACK (on_fetch_image), obj);
767 g_signal_connect (G_OBJECT (obj), "key-release-event",
768 G_CALLBACK (modest_msg_view_window_key_event),
771 g_signal_connect (G_OBJECT (obj), "key-press-event",
772 G_CALLBACK (modest_msg_view_window_key_event),
775 g_signal_connect (G_OBJECT (obj), "move-focus",
776 G_CALLBACK (on_move_focus), obj);
778 g_signal_connect (G_OBJECT (obj), "map-event",
779 G_CALLBACK (_modest_msg_view_window_map_event),
782 /* Mail Operation Queue */
783 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
785 G_CALLBACK (on_queue_changed),
788 /* Account manager */
789 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
791 G_CALLBACK(on_account_removed),
794 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
795 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
797 /* First add out toolbar ... */
798 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
800 /* ... and later the find toolbar. This way find toolbar will
801 be shown over the other */
802 priv->find_toolbar = hildon_find_toolbar_new (NULL);
803 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
804 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
805 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
806 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
807 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
808 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
809 priv->last_search = NULL;
811 /* Init the clipboard actions dim status */
812 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
814 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
819 /* FIXME: parameter checks */
821 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
822 const gchar *modest_account_name,
823 const gchar *mailbox,
824 const gchar *msg_uid,
826 GtkTreeRowReference *row_reference)
828 ModestMsgViewWindow *window = NULL;
829 ModestMsgViewWindowPrivate *priv = NULL;
830 TnyFolder *header_folder = NULL;
831 ModestHeaderView *header_view = NULL;
832 ModestWindowMgr *mgr = NULL;
835 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
838 mgr = modest_runtime_get_window_mgr ();
839 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
840 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
842 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
844 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
846 /* Remember the message list's TreeModel so we can detect changes
847 * and change the list selection when necessary: */
848 header_folder = modest_header_view_get_folder (header_view);
850 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
851 TNY_FOLDER_TYPE_OUTBOX);
852 priv->header_folder_id = tny_folder_get_id (header_folder);
853 g_object_unref(header_folder);
856 /* Setup row references and connect signals */
857 priv->header_model = g_object_ref (model);
860 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
861 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
862 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
864 priv->row_reference = NULL;
865 priv->next_row_reference = NULL;
868 /* Connect signals */
869 priv->row_changed_handler =
870 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
871 G_CALLBACK(modest_msg_view_window_on_row_changed),
873 priv->row_deleted_handler =
874 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
875 G_CALLBACK(modest_msg_view_window_on_row_deleted),
877 priv->row_inserted_handler =
878 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
879 G_CALLBACK(modest_msg_view_window_on_row_inserted),
881 priv->rows_reordered_handler =
882 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
883 G_CALLBACK(modest_msg_view_window_on_row_reordered),
886 if (header_view != NULL){
887 modest_header_view_add_observer(header_view,
888 MODEST_HEADER_VIEW_OBSERVER(window));
891 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
892 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
893 update_branding (MODEST_MSG_VIEW_WINDOW (window));
895 /* gtk_widget_show_all (GTK_WIDGET (window)); */
896 modest_msg_view_window_update_priority (window);
897 /* Check dimming rules */
898 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
899 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
900 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
902 return MODEST_WINDOW(window);
906 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
907 const gchar *mailbox,
908 const gchar *msg_uid)
910 ModestMsgViewWindow *window = NULL;
911 ModestMsgViewWindowPrivate *priv = NULL;
912 ModestWindowMgr *mgr = NULL;
914 TnyAccount *account = NULL;
916 mgr = modest_runtime_get_window_mgr ();
917 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
918 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
920 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
922 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
926 is_merge = g_str_has_prefix (msg_uid, "merge:");
928 /* Get the account */
930 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
934 if (is_merge || account) {
935 TnyFolder *folder = NULL;
937 /* Try to get the message, if it's already downloaded
938 we don't need to connect */
940 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
942 ModestTnyAccountStore *account_store;
943 ModestTnyLocalFoldersAccount *local_folders_account;
945 account_store = modest_runtime_get_account_store ();
946 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
947 modest_tny_account_store_get_local_folders_account (account_store));
948 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
949 g_object_unref (local_folders_account);
953 gboolean device_online;
955 device = modest_runtime_get_device();
956 device_online = tny_device_is_online (device);
958 message_reader (window, priv, NULL, msg_uid, folder, NULL);
960 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
962 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
963 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
964 update_branding (MODEST_MSG_VIEW_WINDOW (window));
965 g_object_unref (msg);
967 message_reader (window, priv, NULL, msg_uid, folder, NULL);
970 g_object_unref (folder);
975 /* Check dimming rules */
976 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
977 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
978 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
980 return MODEST_WINDOW(window);
984 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
985 const gchar *modest_account_name,
986 const gchar *mailbox,
987 const gchar *msg_uid,
988 GtkTreeRowReference *row_reference)
990 ModestMsgViewWindow *window = NULL;
991 ModestMsgViewWindowPrivate *priv = NULL;
992 TnyFolder *header_folder = NULL;
993 ModestWindowMgr *mgr = NULL;
997 mgr = modest_runtime_get_window_mgr ();
998 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
999 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1001 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1003 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1005 /* Remember the message list's TreeModel so we can detect changes
1006 * and change the list selection when necessary: */
1008 if (header_view != NULL){
1009 header_folder = modest_header_view_get_folder(header_view);
1010 /* This could happen if the header folder was
1011 unseleted before opening this msg window (for
1012 example if the user selects an account in the
1013 folder view of the main window */
1014 if (header_folder) {
1015 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1016 TNY_FOLDER_TYPE_OUTBOX);
1017 priv->header_folder_id = tny_folder_get_id(header_folder);
1018 g_object_unref(header_folder);
1022 /* Setup row references and connect signals */
1023 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1024 g_object_ref (priv->header_model);
1026 if (row_reference) {
1027 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1028 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1029 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1031 priv->row_reference = NULL;
1032 priv->next_row_reference = NULL;
1035 /* Connect signals */
1036 priv->row_changed_handler =
1037 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1038 G_CALLBACK(modest_msg_view_window_on_row_changed),
1040 priv->row_deleted_handler =
1041 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1042 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1044 priv->row_inserted_handler =
1045 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1046 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1048 priv->rows_reordered_handler =
1049 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1050 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1053 if (header_view != NULL){
1054 modest_header_view_add_observer(header_view,
1055 MODEST_HEADER_VIEW_OBSERVER(window));
1058 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1059 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1061 path = gtk_tree_row_reference_get_path (row_reference);
1062 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1064 gtk_tree_model_get (priv->header_model, &iter,
1065 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1067 message_reader (window, priv, header, NULL, NULL, row_reference);
1068 g_object_unref (header);
1070 gtk_tree_path_free (path);
1072 /* Check dimming rules */
1073 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1074 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1075 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1077 return MODEST_WINDOW(window);
1081 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1082 const gchar *modest_account_name,
1083 const gchar *mailbox,
1084 const gchar *msg_uid)
1086 ModestMsgViewWindow *window = NULL;
1087 ModestMsgViewWindowPrivate *priv = NULL;
1088 ModestWindowMgr *mgr = NULL;
1090 mgr = modest_runtime_get_window_mgr ();
1091 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1092 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1093 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1095 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1097 /* Remember that this is a search result,
1098 * so we can disable some UI appropriately: */
1099 priv->is_search_result = TRUE;
1101 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1102 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1104 update_window_title (window);
1105 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1106 modest_msg_view_window_update_priority (window);
1108 /* Check dimming rules */
1109 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1110 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1111 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1113 return MODEST_WINDOW(window);
1117 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1119 ModestMsgViewWindowPrivate *priv = NULL;
1121 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1122 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1124 return (priv->other_body != NULL);
1128 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1129 TnyMimePart *other_body,
1130 const gchar *modest_account_name,
1131 const gchar *mailbox,
1132 const gchar *msg_uid)
1134 GObject *obj = NULL;
1135 ModestMsgViewWindowPrivate *priv;
1136 ModestWindowMgr *mgr = NULL;
1138 g_return_val_if_fail (msg, NULL);
1139 mgr = modest_runtime_get_window_mgr ();
1140 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1141 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1142 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1143 modest_account_name, mailbox, msg_uid);
1146 priv->other_body = g_object_ref (other_body);
1147 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1149 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1151 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1152 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1154 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1156 /* Check dimming rules */
1157 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1158 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1159 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1161 return MODEST_WINDOW(obj);
1165 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1166 const gchar *modest_account_name,
1167 const gchar *mailbox,
1168 const gchar *msg_uid)
1170 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1174 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1177 ModestMsgViewWindow *window)
1179 check_dimming_rules_after_change (window);
1183 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1185 ModestMsgViewWindow *window)
1187 check_dimming_rules_after_change (window);
1189 /* The window could have dissapeared */
1192 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1194 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1195 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1199 /* On insertions we check if the folder still has the message we are
1200 * showing or do not. If do not, we do nothing. Which means we are still
1201 * not attached to any header folder and thus next/prev buttons are
1202 * still dimmed. Once the message that is shown by msg-view is found, the
1203 * new model of header-view will be attached and the references will be set.
1204 * On each further insertions dimming rules will be checked. However
1205 * this requires extra CPU time at least works.
1206 * (An message might be deleted from TnyFolder and thus will not be
1207 * inserted into the model again for example if it is removed by the
1208 * imap server and the header view is refreshed.)
1211 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1212 GtkTreePath *tree_path,
1213 GtkTreeIter *tree_iter,
1214 ModestMsgViewWindow *window)
1216 ModestMsgViewWindowPrivate *priv = NULL;
1217 TnyHeader *header = NULL;
1219 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1220 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1222 g_assert (model == priv->header_model);
1224 /* Check if the newly inserted message is the same we are actually
1225 * showing. IF not, we should remain detached from the header model
1226 * and thus prev and next toolbar buttons should remain dimmed. */
1227 gtk_tree_model_get (model, tree_iter,
1228 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1231 if (TNY_IS_HEADER (header)) {
1234 uid = modest_tny_folder_get_header_unique_id (header);
1235 if (!g_str_equal(priv->msg_uid, uid)) {
1236 check_dimming_rules_after_change (window);
1238 g_object_unref (G_OBJECT(header));
1242 g_object_unref(G_OBJECT(header));
1245 if (priv->row_reference) {
1246 gtk_tree_row_reference_free (priv->row_reference);
1249 /* Setup row_reference for the actual msg. */
1250 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1251 if (priv->row_reference == NULL) {
1252 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1256 /* Now set up next_row_reference. */
1257 if (priv->next_row_reference) {
1258 gtk_tree_row_reference_free (priv->next_row_reference);
1261 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1262 select_next_valid_row (priv->header_model,
1263 &(priv->next_row_reference), FALSE, priv->is_outbox);
1265 /* Connect the remaining callbacks to become able to detect
1266 * changes in header-view. */
1267 priv->row_changed_handler =
1268 g_signal_connect (priv->header_model, "row-changed",
1269 G_CALLBACK (modest_msg_view_window_on_row_changed),
1271 priv->row_deleted_handler =
1272 g_signal_connect (priv->header_model, "row-deleted",
1273 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1275 priv->rows_reordered_handler =
1276 g_signal_connect (priv->header_model, "rows-reordered",
1277 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1280 check_dimming_rules_after_change (window);
1284 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1288 ModestMsgViewWindow *window)
1290 ModestMsgViewWindowPrivate *priv = NULL;
1291 gboolean already_changed = FALSE;
1293 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1295 /* If the current row was reordered select the proper next
1296 valid row. The same if the next row reference changes */
1297 if (!priv->row_reference ||
1298 !gtk_tree_row_reference_valid (priv->row_reference))
1301 if (priv->next_row_reference &&
1302 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1303 GtkTreePath *cur, *next;
1304 /* Check that the order is still the correct one */
1305 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1306 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1307 gtk_tree_path_next (cur);
1308 if (gtk_tree_path_compare (cur, next) != 0) {
1309 gtk_tree_row_reference_free (priv->next_row_reference);
1310 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1311 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1312 already_changed = TRUE;
1314 gtk_tree_path_free (cur);
1315 gtk_tree_path_free (next);
1317 if (priv->next_row_reference)
1318 gtk_tree_row_reference_free (priv->next_row_reference);
1319 /* Update next row reference */
1320 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1321 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1322 already_changed = TRUE;
1325 check_dimming_rules_after_change (window);
1328 /* The modest_msg_view_window_update_model_replaced implements update
1329 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1330 * actually belongs to the header-view is the same as the TnyFolder of
1331 * the message of msg-view or not. If they are different, there is
1332 * nothing to do. If they are the same, then the model has replaced and
1333 * the reference in msg-view shall be replaced from the old model to
1334 * the new model. In this case the view will be detached from it's
1335 * header folder. From this point the next/prev buttons are dimmed.
1338 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1339 GtkTreeModel *model,
1340 const gchar *tny_folder_id)
1342 ModestMsgViewWindowPrivate *priv = NULL;
1343 ModestMsgViewWindow *window = NULL;
1345 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1346 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1348 window = MODEST_MSG_VIEW_WINDOW(observer);
1349 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1351 /* If there is an other folder in the header-view then we do
1352 * not care about it's model (msg list). Else if the
1353 * header-view shows the folder the msg shown by us is in, we
1354 * shall replace our model reference and make some check. */
1355 if(model == NULL || tny_folder_id == NULL ||
1356 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1359 /* Model is changed(replaced), so we should forget the old
1360 * one. Because there might be other references and there
1361 * might be some change on the model even if we unreferenced
1362 * it, we need to disconnect our signals here. */
1363 if (priv->header_model) {
1364 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1365 priv->row_changed_handler))
1366 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1367 priv->row_changed_handler);
1368 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1369 priv->row_deleted_handler))
1370 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1371 priv->row_deleted_handler);
1372 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1373 priv->row_inserted_handler))
1374 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1375 priv->row_inserted_handler);
1376 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1377 priv->rows_reordered_handler))
1378 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1379 priv->rows_reordered_handler);
1382 if (priv->row_reference)
1383 gtk_tree_row_reference_free (priv->row_reference);
1384 if (priv->next_row_reference)
1385 gtk_tree_row_reference_free (priv->next_row_reference);
1386 g_object_unref(priv->header_model);
1389 priv->row_changed_handler = 0;
1390 priv->row_deleted_handler = 0;
1391 priv->row_inserted_handler = 0;
1392 priv->rows_reordered_handler = 0;
1393 priv->next_row_reference = NULL;
1394 priv->row_reference = NULL;
1395 priv->header_model = NULL;
1398 priv->header_model = g_object_ref (model);
1400 /* Also we must connect to the new model for row insertions.
1401 * Only for insertions now. We will need other ones only after
1402 * the msg is show by msg-view is added to the new model. */
1403 priv->row_inserted_handler =
1404 g_signal_connect (priv->header_model, "row-inserted",
1405 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1408 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1409 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1413 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1415 ModestMsgViewWindowPrivate *priv= NULL;
1417 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1418 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1420 return priv->progress_hint;
1424 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1426 ModestMsgViewWindowPrivate *priv= NULL;
1428 TnyHeader *header = NULL;
1429 GtkTreePath *path = NULL;
1432 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1433 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1435 /* If the message was not obtained from a treemodel,
1436 * for instance if it was opened directly by the search UI:
1438 if (priv->header_model == NULL ||
1439 priv->row_reference == NULL ||
1440 !gtk_tree_row_reference_valid (priv->row_reference)) {
1441 msg = modest_msg_view_window_get_message (self);
1443 header = tny_msg_get_header (msg);
1444 g_object_unref (msg);
1449 /* Get iter of the currently selected message in the header view: */
1450 path = gtk_tree_row_reference_get_path (priv->row_reference);
1451 g_return_val_if_fail (path != NULL, NULL);
1452 gtk_tree_model_get_iter (priv->header_model,
1456 /* Get current message header */
1457 gtk_tree_model_get (priv->header_model, &iter,
1458 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1461 gtk_tree_path_free (path);
1466 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1468 ModestMsgViewWindowPrivate *priv;
1470 g_return_val_if_fail (self, NULL);
1472 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1474 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1478 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1480 ModestMsgViewWindowPrivate *priv;
1482 g_return_val_if_fail (self, NULL);
1484 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1486 return (const gchar*) priv->msg_uid;
1489 /* Used for the Ctrl+F accelerator */
1491 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1494 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1495 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1497 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1498 modest_msg_view_window_find_toolbar_close (obj, data);
1500 modest_msg_view_window_show_find_toolbar (obj, data);
1504 /* Handler for menu option */
1506 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1509 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1510 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1512 gtk_widget_show (priv->find_toolbar);
1513 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1516 /* Handler for click on the "X" close button in find toolbar */
1518 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1519 ModestMsgViewWindow *obj)
1521 ModestMsgViewWindowPrivate *priv;
1523 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1526 gtk_widget_hide (priv->find_toolbar);
1527 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1531 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1532 ModestMsgViewWindow *obj)
1534 gchar *current_search;
1535 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1537 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1538 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1542 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1544 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1545 g_free (current_search);
1546 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1550 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1552 g_free (priv->last_search);
1553 priv->last_search = g_strdup (current_search);
1554 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1557 hildon_banner_show_information (NULL, NULL,
1558 _HL("ckct_ib_find_no_matches"));
1559 g_free (priv->last_search);
1560 priv->last_search = NULL;
1562 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1565 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1566 hildon_banner_show_information (NULL, NULL,
1567 _HL("ckct_ib_find_search_complete"));
1568 g_free (priv->last_search);
1569 priv->last_search = NULL;
1571 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1575 g_free (current_search);
1580 modest_msg_view_window_set_zoom (ModestWindow *window,
1583 ModestMsgViewWindowPrivate *priv;
1584 ModestWindowPrivate *parent_priv;
1586 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1588 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1589 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1590 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1595 modest_msg_view_window_get_zoom (ModestWindow *window)
1597 ModestMsgViewWindowPrivate *priv;
1599 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1601 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1602 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1606 modest_msg_view_window_zoom_plus (ModestWindow *window)
1609 ModestMsgViewWindowPrivate *priv;
1613 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1614 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1616 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1618 if (zoom_level >= 2.0) {
1619 hildon_banner_show_information (NULL, NULL,
1620 _CS("ckct_ib_max_zoom_level_reached"));
1622 } else if (zoom_level >= 1.5) {
1624 } else if (zoom_level >= 1.2) {
1626 } else if (zoom_level >= 1.0) {
1628 } else if (zoom_level >= 0.8) {
1630 } else if (zoom_level >= 0.5) {
1636 /* set zoom level */
1637 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1638 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1639 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1640 g_free (banner_text);
1641 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1647 modest_msg_view_window_zoom_minus (ModestWindow *window)
1650 ModestMsgViewWindowPrivate *priv;
1654 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1655 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1657 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1659 if (zoom_level <= 0.5) {
1660 hildon_banner_show_information (NULL, NULL,
1661 _CS("ckct_ib_min_zoom_level_reached"));
1663 } else if (zoom_level <= 0.8) {
1665 } else if (zoom_level <= 1.0) {
1667 } else if (zoom_level <= 1.2) {
1669 } else if (zoom_level <= 1.5) {
1671 } else if (zoom_level <= 2.0) {
1677 /* set zoom level */
1678 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1679 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1680 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1681 g_free (banner_text);
1682 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1689 modest_msg_view_window_key_event (GtkWidget *window,
1695 focus = gtk_window_get_focus (GTK_WINDOW (window));
1697 /* for the find toolbar case */
1698 if (focus && GTK_IS_ENTRY (focus)) {
1699 if (event->keyval == GDK_BackSpace) {
1701 copy = gdk_event_copy ((GdkEvent *) event);
1702 gtk_widget_event (focus, copy);
1703 gdk_event_free (copy);
1708 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1709 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1710 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1711 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1712 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1713 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1714 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1715 /* gboolean return_value; */
1717 if (event->type == GDK_KEY_PRESS) {
1718 GtkScrollType scroll_type;
1720 switch (event->keyval) {
1723 scroll_type = GTK_SCROLL_STEP_UP; break;
1726 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1728 case GDK_KP_Page_Up:
1729 scroll_type = GTK_SCROLL_PAGE_UP; break;
1731 case GDK_KP_Page_Down:
1732 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1735 scroll_type = GTK_SCROLL_START; break;
1738 scroll_type = GTK_SCROLL_END; break;
1739 default: scroll_type = GTK_SCROLL_NONE;
1742 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1743 /* scroll_type, FALSE, &return_value); */
1754 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1757 ModestMsgViewWindowPrivate *priv;
1758 GtkTreeIter tmp_iter;
1759 gboolean is_last_selected;
1761 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1762 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1764 /*if no model (so no rows at all), then virtually we are the last*/
1765 if (!priv->header_model || !priv->row_reference)
1768 if (!gtk_tree_row_reference_valid (priv->row_reference))
1771 path = gtk_tree_row_reference_get_path (priv->row_reference);
1775 is_last_selected = TRUE;
1776 while (is_last_selected) {
1778 gtk_tree_path_next (path);
1779 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1781 gtk_tree_model_get (priv->header_model, &tmp_iter,
1782 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1785 if (msg_is_visible (header, priv->is_outbox))
1786 is_last_selected = FALSE;
1787 g_object_unref(G_OBJECT(header));
1790 gtk_tree_path_free (path);
1791 return is_last_selected;
1795 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1797 ModestMsgViewWindowPrivate *priv;
1799 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1800 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1802 return priv->header_model != NULL;
1806 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1808 ModestMsgViewWindowPrivate *priv;
1810 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1811 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1813 return priv->is_search_result;
1817 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1819 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1821 if (!check_outbox) {
1824 ModestTnySendQueueStatus status;
1825 status = modest_tny_all_send_queues_get_msg_status (header);
1826 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1827 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1832 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1835 ModestMsgViewWindowPrivate *priv;
1836 gboolean is_first_selected;
1837 GtkTreeIter tmp_iter;
1839 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1840 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1842 /*if no model (so no rows at all), then virtually we are the first*/
1843 if (!priv->header_model || !priv->row_reference)
1846 if (!gtk_tree_row_reference_valid (priv->row_reference))
1849 path = gtk_tree_row_reference_get_path (priv->row_reference);
1853 is_first_selected = TRUE;
1854 while (is_first_selected) {
1856 if(!gtk_tree_path_prev (path))
1858 /* Here the 'if' is needless for logic, but let make sure
1859 * iter is valid for gtk_tree_model_get. */
1860 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1862 gtk_tree_model_get (priv->header_model, &tmp_iter,
1863 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1866 if (msg_is_visible (header, priv->is_outbox))
1867 is_first_selected = FALSE;
1868 g_object_unref(G_OBJECT(header));
1871 gtk_tree_path_free (path);
1872 return is_first_selected;
1879 GtkTreeRowReference *row_reference;
1883 message_reader_performer (gboolean canceled,
1885 GtkWindow *parent_window,
1886 TnyAccount *account,
1889 ModestMailOperation *mail_op = NULL;
1890 MsgReaderInfo *info;
1892 info = (MsgReaderInfo *) user_data;
1893 if (canceled || err) {
1894 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1898 /* Register the header - it'll be unregistered in the callback */
1900 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1902 /* New mail operation */
1903 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1904 modest_ui_actions_disk_operations_error_handler,
1907 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1909 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1911 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1912 g_object_unref (mail_op);
1914 /* Update dimming rules */
1915 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1916 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1919 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1920 g_free (info->msg_uid);
1922 g_object_unref (info->folder);
1924 g_object_unref (info->header);
1925 g_slice_free (MsgReaderInfo, info);
1930 * Reads the message whose summary item is @header. It takes care of
1931 * several things, among others:
1933 * If the message was not previously downloaded then ask the user
1934 * before downloading. If there is no connection launch the connection
1935 * dialog. Update toolbar dimming rules.
1937 * Returns: TRUE if the mail operation was started, otherwise if the
1938 * user do not want to download the message, or if the user do not
1939 * want to connect, then the operation is not issued
1942 message_reader (ModestMsgViewWindow *window,
1943 ModestMsgViewWindowPrivate *priv,
1945 const gchar *msg_uid,
1947 GtkTreeRowReference *row_reference)
1949 ModestWindowMgr *mgr;
1950 TnyAccount *account;
1951 MsgReaderInfo *info;
1953 /* We set the header from model while we're loading */
1954 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1955 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1961 g_object_ref (folder);
1963 mgr = modest_runtime_get_window_mgr ();
1964 /* Msg download completed */
1965 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1967 /* Ask the user if he wants to download the message if
1969 if (!tny_device_is_online (modest_runtime_get_device())) {
1970 GtkResponseType response;
1972 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1973 _("mcen_nc_get_msg"));
1974 if (response == GTK_RESPONSE_CANCEL) {
1975 update_window_title (window);
1980 folder = tny_header_get_folder (header);
1982 info = g_slice_new (MsgReaderInfo);
1983 info->msg_uid = g_strdup (msg_uid);
1985 info->header = g_object_ref (header);
1987 info->header = NULL;
1989 info->folder = g_object_ref (folder);
1991 info->folder = NULL;
1992 if (row_reference) {
1993 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1995 info->row_reference = NULL;
1998 /* Offer the connection dialog if necessary */
1999 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2001 TNY_FOLDER_STORE (folder),
2002 message_reader_performer,
2004 g_object_unref (folder);
2010 folder = tny_header_get_folder (header);
2012 account = tny_folder_get_account (folder);
2013 info = g_slice_new (MsgReaderInfo);
2014 info->msg_uid = g_strdup (msg_uid);
2016 info->folder = g_object_ref (folder);
2018 info->folder = NULL;
2020 info->header = g_object_ref (header);
2022 info->header = NULL;
2024 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2026 info->row_reference = NULL;
2028 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2029 g_object_unref (account);
2031 g_object_unref (folder);
2037 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2039 ModestMsgViewWindowPrivate *priv;
2040 GtkTreePath *path= NULL;
2041 GtkTreeIter tmp_iter;
2043 gboolean retval = TRUE;
2044 GtkTreeRowReference *row_reference = NULL;
2046 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2047 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2049 if (!priv->row_reference)
2052 /* Update the next row reference if it's not valid. This could
2053 happen if for example the header which it was pointing to,
2054 was deleted. The best place to do it is in the row-deleted
2055 handler but the tinymail model do not work like the glib
2056 tree models and reports the deletion when the row is still
2058 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2059 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2060 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2061 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2064 if (priv->next_row_reference)
2065 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2069 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2071 gtk_tree_model_get_iter (priv->header_model,
2074 gtk_tree_path_free (path);
2076 gtk_tree_model_get (priv->header_model, &tmp_iter,
2077 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2080 /* Read the message & show it */
2081 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2084 gtk_tree_row_reference_free (row_reference);
2087 g_object_unref (header);
2093 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2095 ModestMsgViewWindowPrivate *priv = NULL;
2097 gboolean finished = FALSE;
2098 gboolean retval = FALSE;
2100 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2101 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2103 /* Return inmediatly if there is no header model */
2104 if (!priv->header_model || !priv->row_reference)
2107 path = gtk_tree_row_reference_get_path (priv->row_reference);
2108 while (!finished && gtk_tree_path_prev (path)) {
2112 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2113 gtk_tree_model_get (priv->header_model, &iter,
2114 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2118 if (msg_is_visible (header, priv->is_outbox)) {
2119 GtkTreeRowReference *row_reference;
2120 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2121 /* Read the message & show it */
2122 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2123 gtk_tree_row_reference_free (row_reference);
2127 g_object_unref (header);
2131 gtk_tree_path_free (path);
2136 view_msg_cb (ModestMailOperation *mail_op,
2143 ModestMsgViewWindow *self = NULL;
2144 ModestMsgViewWindowPrivate *priv = NULL;
2145 GtkTreeRowReference *row_reference = NULL;
2147 /* Unregister the header (it was registered before creating the mail operation) */
2148 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2150 row_reference = (GtkTreeRowReference *) user_data;
2153 gtk_tree_row_reference_free (row_reference);
2154 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2156 /* Restore window title */
2157 update_window_title (self);
2158 g_object_unref (self);
2163 /* If there was any error */
2164 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2166 gtk_tree_row_reference_free (row_reference);
2167 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2169 /* Restore window title */
2170 update_window_title (self);
2171 g_object_unref (self);
2176 /* Get the window */
2177 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2178 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2179 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2181 /* Update the row reference */
2182 if (priv->row_reference != NULL) {
2183 gtk_tree_row_reference_free (priv->row_reference);
2184 priv->row_reference = row_reference?gtk_tree_row_reference_copy (row_reference):NULL;
2185 if (priv->next_row_reference != NULL) {
2186 gtk_tree_row_reference_free (priv->next_row_reference);
2188 if (row_reference) {
2189 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2190 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2192 priv->next_row_reference = NULL;
2196 /* Mark header as read */
2197 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2198 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2200 /* Set new message */
2201 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2202 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2203 modest_msg_view_window_update_priority (self);
2204 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2205 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2206 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2209 /* Set the new message uid of the window */
2210 if (priv->msg_uid) {
2211 g_free (priv->msg_uid);
2212 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2215 /* Notify the observers */
2216 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2217 0, priv->header_model, priv->row_reference);
2220 g_object_unref (self);
2222 gtk_tree_row_reference_free (row_reference);
2226 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2228 ModestMsgViewWindowPrivate *priv;
2230 TnyFolderType folder_type;
2232 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2234 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2236 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2240 folder = tny_msg_get_folder (msg);
2242 folder_type = modest_tny_folder_guess_folder_type (folder);
2243 g_object_unref (folder);
2245 g_object_unref (msg);
2253 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2255 ModestMsgViewWindowPrivate *priv;
2256 TnyHeader *header = NULL;
2257 TnyHeaderFlags flags = 0;
2259 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2261 if (priv->header_model && priv->row_reference) {
2263 GtkTreePath *path = NULL;
2265 path = gtk_tree_row_reference_get_path (priv->row_reference);
2266 g_return_if_fail (path != NULL);
2267 gtk_tree_model_get_iter (priv->header_model,
2269 gtk_tree_row_reference_get_path (priv->row_reference));
2271 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2273 gtk_tree_path_free (path);
2276 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2278 header = tny_msg_get_header (msg);
2279 g_object_unref (msg);
2284 flags = tny_header_get_flags (header);
2285 g_object_unref(G_OBJECT(header));
2288 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2293 toolbar_resize (ModestMsgViewWindow *self)
2295 ModestMsgViewWindowPrivate *priv = NULL;
2296 ModestWindowPrivate *parent_priv = NULL;
2298 gint static_button_size;
2299 ModestWindowMgr *mgr;
2301 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2302 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2303 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2305 mgr = modest_runtime_get_window_mgr ();
2306 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2308 if (parent_priv->toolbar) {
2309 /* left size buttons */
2310 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2311 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2312 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2313 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2314 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2315 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2316 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2317 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2318 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2319 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2320 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2321 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2322 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2323 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2324 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2325 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2327 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2328 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2329 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2330 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2335 modest_msg_view_window_show_toolbar (ModestWindow *self,
2336 gboolean show_toolbar)
2338 ModestMsgViewWindowPrivate *priv = NULL;
2339 ModestWindowPrivate *parent_priv;
2341 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2342 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2344 /* Set optimized view status */
2345 priv->optimized_view = !show_toolbar;
2347 if (!parent_priv->toolbar) {
2348 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2350 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2351 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2353 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2354 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2355 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2358 hildon_window_add_toolbar (HILDON_WINDOW (self),
2359 GTK_TOOLBAR (parent_priv->toolbar));
2364 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2365 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2366 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2368 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2369 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2370 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2372 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2375 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2376 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2381 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2383 ModestMsgViewWindow *window)
2385 if (!GTK_WIDGET_VISIBLE (window))
2388 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2392 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2394 ModestMsgViewWindowPrivate *priv;
2396 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2397 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2399 return priv->progress_hint;
2403 observers_empty (ModestMsgViewWindow *self)
2406 ModestMsgViewWindowPrivate *priv;
2407 gboolean is_empty = TRUE;
2408 guint pending_ops = 0;
2410 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2411 tmp = priv->progress_widgets;
2413 /* Check all observers */
2414 while (tmp && is_empty) {
2415 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2416 is_empty = pending_ops == 0;
2418 tmp = g_slist_next(tmp);
2425 on_account_removed (TnyAccountStore *account_store,
2426 TnyAccount *account,
2429 /* Do nothing if it's a transport account, because we only
2430 show the messages of a store account */
2431 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2432 const gchar *parent_acc = NULL;
2433 const gchar *our_acc = NULL;
2435 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2436 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2438 /* Close this window if I'm showing a message of the removed account */
2439 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2440 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2445 on_mail_operation_started (ModestMailOperation *mail_op,
2448 ModestMsgViewWindow *self;
2449 ModestMailOperationTypeOperation op_type;
2451 ModestMsgViewWindowPrivate *priv;
2452 GObject *source = NULL;
2454 self = MODEST_MSG_VIEW_WINDOW (user_data);
2455 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2456 op_type = modest_mail_operation_get_type_operation (mail_op);
2457 tmp = priv->progress_widgets;
2458 source = modest_mail_operation_get_source(mail_op);
2459 if (G_OBJECT (self) == source) {
2460 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2461 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2462 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2463 set_progress_hint (self, TRUE);
2465 modest_progress_object_add_operation (
2466 MODEST_PROGRESS_OBJECT (tmp->data),
2468 tmp = g_slist_next (tmp);
2472 g_object_unref (source);
2474 /* Update dimming rules */
2475 check_dimming_rules_after_change (self);
2479 on_mail_operation_finished (ModestMailOperation *mail_op,
2482 ModestMsgViewWindow *self;
2483 ModestMailOperationTypeOperation op_type;
2485 ModestMsgViewWindowPrivate *priv;
2487 self = MODEST_MSG_VIEW_WINDOW (user_data);
2488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2489 op_type = modest_mail_operation_get_type_operation (mail_op);
2490 tmp = priv->progress_widgets;
2492 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2493 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2494 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2496 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2498 tmp = g_slist_next (tmp);
2501 /* If no more operations are being observed, NORMAL mode is enabled again */
2502 if (observers_empty (self)) {
2503 set_progress_hint (self, FALSE);
2507 /* Update dimming rules. We have to do this right here
2508 and not in view_msg_cb because at that point the
2509 transfer mode is still enabled so the dimming rule
2510 won't let the user delete the message that has been
2511 readed for example */
2512 check_dimming_rules_after_change (self);
2516 on_queue_changed (ModestMailOperationQueue *queue,
2517 ModestMailOperation *mail_op,
2518 ModestMailOperationQueueNotification type,
2519 ModestMsgViewWindow *self)
2521 ModestMsgViewWindowPrivate *priv;
2523 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2525 /* If this operations was created by another window, do nothing */
2526 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2529 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2530 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2532 "operation-started",
2533 G_CALLBACK (on_mail_operation_started),
2535 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2537 "operation-finished",
2538 G_CALLBACK (on_mail_operation_finished),
2540 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2541 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2543 "operation-started");
2544 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2546 "operation-finished");
2551 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2553 ModestMsgViewWindowPrivate *priv;
2554 TnyList *selected_attachments = NULL;
2556 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2557 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2559 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2560 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2562 return selected_attachments;
2566 ModestMsgViewWindow *self;
2568 } DecodeAsyncHelper;
2571 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2577 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2579 /* It could happen that the window was closed */
2580 if (GTK_WIDGET_VISIBLE (helper->self))
2581 set_progress_hint (helper->self, FALSE);
2583 if (cancelled || err) {
2585 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2586 modest_platform_information_banner (NULL, NULL, msg);
2592 /* make the file read-only */
2593 g_chmod(helper->file_path, 0444);
2595 /* Activate the file */
2596 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2600 g_object_unref (helper->self);
2601 g_free (helper->file_path);
2602 g_slice_free (DecodeAsyncHelper, helper);
2606 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2607 TnyMimePart *mime_part)
2609 ModestMsgViewWindowPrivate *priv;
2610 const gchar *msg_uid;
2611 gchar *attachment_uid = NULL;
2612 gint attachment_index = 0;
2613 TnyList *attachments;
2615 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2616 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2617 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2619 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2620 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2621 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2622 g_object_unref (attachments);
2624 if (msg_uid && attachment_index >= 0) {
2625 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2628 if (mime_part == NULL) {
2629 gboolean error = FALSE;
2630 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2631 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2633 } else if (tny_list_get_length (selected_attachments) > 1) {
2634 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2638 iter = tny_list_create_iterator (selected_attachments);
2639 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2640 g_object_unref (iter);
2642 if (selected_attachments)
2643 g_object_unref (selected_attachments);
2648 g_object_ref (mime_part);
2651 if (tny_mime_part_is_purged (mime_part))
2654 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2655 gchar *filepath = NULL;
2656 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2657 gboolean show_error_banner = FALSE;
2658 TnyFsStream *temp_stream = NULL;
2659 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2662 if (temp_stream != NULL) {
2663 DecodeAsyncHelper *helper;
2665 /* Activate progress hint */
2666 set_progress_hint (window, TRUE);
2668 helper = g_slice_new0 (DecodeAsyncHelper);
2669 helper->self = g_object_ref (window);
2670 helper->file_path = g_strdup (filepath);
2672 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2673 on_decode_to_stream_async_handler,
2676 g_object_unref (temp_stream);
2677 /* NOTE: files in the temporary area will be automatically
2678 * cleaned after some time if they are no longer in use */
2681 const gchar *content_type;
2682 /* the file may already exist but it isn't writable,
2683 * let's try to open it anyway */
2684 content_type = tny_mime_part_get_content_type (mime_part);
2685 modest_platform_activate_file (filepath, content_type);
2687 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2688 show_error_banner = TRUE;
2693 if (show_error_banner)
2694 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2695 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2696 ModestWindowMgr *mgr;
2697 ModestWindow *msg_win = NULL;
2698 TnyMsg *current_msg;
2702 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2703 mgr = modest_runtime_get_window_mgr ();
2704 header = tny_msg_get_header (TNY_MSG (current_msg));
2705 found = modest_window_mgr_find_registered_message_uid (mgr,
2710 g_debug ("window for this body is already being created");
2713 /* it's not found, so create a new window for it */
2714 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2715 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2716 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2718 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2720 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2721 account, mailbox, attachment_uid);
2723 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2724 modest_window_get_zoom (MODEST_WINDOW (window)));
2725 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2726 gtk_widget_show_all (GTK_WIDGET (msg_win));
2728 gtk_widget_destroy (GTK_WIDGET (msg_win));
2730 g_object_unref (current_msg);
2732 /* message attachment */
2733 TnyHeader *header = NULL;
2734 ModestWindowMgr *mgr;
2735 ModestWindow *msg_win = NULL;
2738 header = tny_msg_get_header (TNY_MSG (mime_part));
2739 mgr = modest_runtime_get_window_mgr ();
2740 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2743 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2744 * thus, we don't do anything */
2745 g_debug ("window for is already being created");
2747 /* it's not found, so create a new window for it */
2748 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2749 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2750 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2752 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2753 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2754 mailbox, attachment_uid);
2755 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2756 modest_window_get_zoom (MODEST_WINDOW (window)));
2757 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2758 gtk_widget_show_all (GTK_WIDGET (msg_win));
2760 gtk_widget_destroy (GTK_WIDGET (msg_win));
2766 g_free (attachment_uid);
2768 g_object_unref (mime_part);
2780 GnomeVFSResult result;
2784 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2785 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2786 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2787 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2790 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2794 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2795 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2796 g_free (pair->filename);
2797 g_object_unref (pair->part);
2798 g_slice_free (SaveMimePartPair, pair);
2800 g_list_free (info->pairs);
2804 g_slice_free (SaveMimePartInfo, info);
2809 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2811 /* This is a GDK lock because we are an idle callback and
2812 * hildon_banner_show_information is or does Gtk+ code */
2814 gdk_threads_enter (); /* CHECKED */
2815 if (info->result == GNOME_VFS_OK) {
2816 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2817 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2820 /* Check if the uri belongs to the external mmc */
2821 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2822 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2824 msg = g_strdup (_KR("cerm_memory_card_full"));
2825 modest_platform_information_banner (NULL, NULL, msg);
2828 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2830 save_mime_part_info_free (info, FALSE);
2831 gdk_threads_leave (); /* CHECKED */
2837 save_mime_part_to_file (SaveMimePartInfo *info)
2839 GnomeVFSHandle *handle;
2841 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2843 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2844 if (info->result == GNOME_VFS_OK) {
2845 GError *error = NULL;
2846 stream = tny_vfs_stream_new (handle);
2847 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2848 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2850 if ((error->domain == TNY_ERROR_DOMAIN) &&
2851 (error->code == TNY_IO_ERROR_WRITE) &&
2852 (errno == ENOSPC)) {
2853 info->result = GNOME_VFS_ERROR_NO_SPACE;
2855 info->result = GNOME_VFS_ERROR_IO;
2858 g_object_unref (G_OBJECT (stream));
2860 g_warning ("Could not create save attachment %s: %s\n",
2861 pair->filename, gnome_vfs_result_to_string (info->result));
2864 /* Go on saving remaining files */
2865 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2866 if (info->pairs != NULL) {
2867 save_mime_part_to_file (info);
2869 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2876 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2877 SaveMimePartInfo *info)
2879 gboolean is_ok = TRUE;
2880 gint replaced_files = 0;
2881 const GList *files = info->pairs;
2882 const GList *iter, *to_replace = NULL;
2884 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2885 SaveMimePartPair *pair = iter->data;
2886 if (modest_utils_file_exists (pair->filename)) {
2888 if (replaced_files == 1)
2892 if (replaced_files) {
2895 if (replaced_files == 1) {
2896 SaveMimePartPair *pair = to_replace->data;
2897 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2898 gchar *escaped_basename, *message;
2900 escaped_basename = g_uri_unescape_string (basename, NULL);
2901 message = g_strdup_printf ("%s\n%s",
2902 _FM("docm_nc_replace_file"),
2903 (escaped_basename) ? escaped_basename : "");
2904 response = modest_platform_run_confirmation_dialog (parent, message);
2906 g_free (escaped_basename);
2908 response = modest_platform_run_confirmation_dialog (parent,
2909 _FM("docm_nc_replace_multiple"));
2911 if (response != GTK_RESPONSE_OK)
2916 save_mime_part_info_free (info, TRUE);
2918 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2924 save_attachments_response (GtkDialog *dialog,
2928 TnyList *mime_parts;
2930 GList *files_to_save = NULL;
2931 gchar *current_folder;
2933 mime_parts = TNY_LIST (user_data);
2935 if (arg1 != GTK_RESPONSE_OK)
2938 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2939 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2940 if (current_folder && *current_folder != '\0') {
2942 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2943 current_folder,&err);
2945 g_debug ("Error storing latest used folder: %s", err->message);
2949 g_free (current_folder);
2951 if (!modest_utils_folder_writable (chooser_uri)) {
2952 const gchar *err_msg;
2954 #ifdef MODEST_PLATFORM_MAEMO
2955 if (modest_maemo_utils_in_usb_mode ()) {
2956 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
2958 err_msg = _FM("sfil_ib_readonly_location");
2961 err_msg = _FM("sfil_ib_readonly_location");
2963 hildon_banner_show_information (NULL, NULL, err_msg);
2967 iter = tny_list_create_iterator (mime_parts);
2968 while (!tny_iterator_is_done (iter)) {
2969 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2971 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2972 !tny_mime_part_is_purged (mime_part) &&
2973 (tny_mime_part_get_filename (mime_part) != NULL)) {
2974 SaveMimePartPair *pair;
2976 pair = g_slice_new0 (SaveMimePartPair);
2978 if (tny_list_get_length (mime_parts) > 1) {
2980 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2981 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2984 pair->filename = g_strdup (chooser_uri);
2986 pair->part = mime_part;
2987 files_to_save = g_list_prepend (files_to_save, pair);
2989 tny_iterator_next (iter);
2991 g_object_unref (iter);
2994 if (files_to_save != NULL) {
2995 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2996 info->pairs = files_to_save;
2997 info->result = TRUE;
2998 info->uri = g_strdup (chooser_uri);
2999 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3001 g_free (chooser_uri);
3004 /* Free and close the dialog */
3005 g_object_unref (mime_parts);
3006 gtk_widget_destroy (GTK_WIDGET (dialog));
3010 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3011 TnyList *mime_parts)
3013 ModestMsgViewWindowPrivate *priv;
3014 GtkWidget *save_dialog = NULL;
3015 gchar *conf_folder = NULL;
3016 gchar *filename = NULL;
3017 gchar *save_multiple_str = NULL;
3018 const gchar *root_folder = "file:///";
3020 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3021 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3023 if (mime_parts == NULL) {
3024 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3025 * selection available */
3026 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3027 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3028 g_object_unref (mime_parts);
3031 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3033 g_object_unref (mime_parts);
3039 g_object_ref (mime_parts);
3042 /* prepare dialog */
3043 if (tny_list_get_length (mime_parts) == 1) {
3045 /* only one attachment selected */
3046 iter = tny_list_create_iterator (mime_parts);
3047 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3048 g_object_unref (iter);
3049 if (!modest_tny_mime_part_is_msg (mime_part) &&
3050 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3051 !tny_mime_part_is_purged (mime_part)) {
3052 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3054 /* TODO: show any error? */
3055 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3056 g_object_unref (mime_parts);
3059 g_object_unref (mime_part);
3061 gint num = tny_list_get_length (mime_parts);
3062 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3063 "sfil_va_number_of_objects_attachment",
3064 "sfil_va_number_of_objects_attachments",
3068 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3069 GTK_FILE_CHOOSER_ACTION_SAVE);
3071 /* Get last used folder */
3072 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3073 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3075 /* File chooser stops working if we select "file:///" as current folder */
3076 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3077 g_free (conf_folder);
3081 if (conf_folder && conf_folder[0] != '\0') {
3082 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3085 /* Set the default folder to documents folder */
3086 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3089 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3091 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3092 g_free (docs_folder);
3094 g_free (conf_folder);
3098 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3103 /* if multiple, set multiple string */
3104 if (save_multiple_str) {
3105 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3106 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3107 g_free (save_multiple_str);
3110 /* We must run this asynchronously, because the hildon dialog
3111 performs a gtk_dialog_run by itself which leads to gdk
3113 g_signal_connect (save_dialog, "response",
3114 G_CALLBACK (save_attachments_response), mime_parts);
3116 gtk_widget_show_all (save_dialog);
3120 show_remove_attachment_information (gpointer userdata)
3122 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3123 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3125 /* We're outside the main lock */
3126 gdk_threads_enter ();
3128 if (priv->remove_attachment_banner != NULL) {
3129 gtk_widget_destroy (priv->remove_attachment_banner);
3130 g_object_unref (priv->remove_attachment_banner);
3133 priv->remove_attachment_banner = g_object_ref (
3134 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3136 gdk_threads_leave ();
3142 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3144 ModestMsgViewWindowPrivate *priv;
3145 TnyList *mime_parts = NULL, *tmp;
3146 gchar *confirmation_message;
3152 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3155 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3156 * because we don't have selection
3158 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3160 /* Remove already purged messages from mime parts list. We use
3161 a copy of the list to remove items in the original one */
3162 tmp = tny_list_copy (mime_parts);
3163 iter = tny_list_create_iterator (tmp);
3164 while (!tny_iterator_is_done (iter)) {
3165 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3166 if (tny_mime_part_is_purged (part))
3167 tny_list_remove (mime_parts, (GObject *) part);
3169 g_object_unref (part);
3170 tny_iterator_next (iter);
3172 g_object_unref (tmp);
3173 g_object_unref (iter);
3175 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3176 tny_list_get_length (mime_parts) == 0) {
3177 g_object_unref (mime_parts);
3181 n_attachments = tny_list_get_length (mime_parts);
3182 if (n_attachments == 1) {
3186 iter = tny_list_create_iterator (mime_parts);
3187 part = (TnyMimePart *) tny_iterator_get_current (iter);
3188 g_object_unref (iter);
3189 if (modest_tny_mime_part_is_msg (part)) {
3191 header = tny_msg_get_header (TNY_MSG (part));
3192 filename = tny_header_dup_subject (header);
3193 g_object_unref (header);
3194 if (filename == NULL)
3195 filename = g_strdup (_("mail_va_no_subject"));
3197 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3199 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3201 g_object_unref (part);
3203 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3204 "mcen_nc_purge_files_text",
3205 n_attachments), n_attachments);
3207 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3208 confirmation_message);
3209 g_free (confirmation_message);
3211 if (response != GTK_RESPONSE_OK) {
3212 g_object_unref (mime_parts);
3216 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3218 iter = tny_list_create_iterator (mime_parts);
3219 while (!tny_iterator_is_done (iter)) {
3222 part = (TnyMimePart *) tny_iterator_get_current (iter);
3223 tny_mime_part_set_purged (TNY_MIME_PART (part));
3224 g_object_unref (part);
3225 tny_iterator_next (iter);
3227 g_object_unref (iter);
3229 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3230 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3231 tny_msg_rewrite_cache (msg);
3232 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3233 g_object_unref (msg);
3234 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3236 g_object_unref (mime_parts);
3238 if (priv->purge_timeout > 0) {
3239 g_source_remove (priv->purge_timeout);
3240 priv->purge_timeout = 0;
3243 if (priv->remove_attachment_banner) {
3244 gtk_widget_destroy (priv->remove_attachment_banner);
3245 g_object_unref (priv->remove_attachment_banner);
3246 priv->remove_attachment_banner = NULL;
3252 update_window_title (ModestMsgViewWindow *window)
3254 ModestMsgViewWindowPrivate *priv;
3256 TnyHeader *header = NULL;
3257 gchar *subject = NULL;
3259 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3261 /* Note that if the window is closed while we're retrieving
3262 the message, this widget could de deleted */
3263 if (!priv->msg_view)
3266 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3268 if (priv->other_body) {
3271 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3273 g_strstrip (description);
3274 subject = description;
3276 } else if (msg != NULL) {
3277 header = tny_msg_get_header (msg);
3278 subject = tny_header_dup_subject (header);
3279 g_object_unref (header);
3280 g_object_unref (msg);
3283 if ((subject == NULL)||(subject[0] == '\0')) {
3285 subject = g_strdup (_("mail_va_no_subject"));
3288 gtk_window_set_title (GTK_WINDOW (window), subject);
3293 on_move_focus (GtkWidget *widget,
3294 GtkDirectionType direction,
3297 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3301 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3303 GnomeVFSResult result;
3304 GnomeVFSHandle *handle = NULL;
3305 GnomeVFSFileInfo *info = NULL;
3308 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3309 if (result != GNOME_VFS_OK) {
3314 info = gnome_vfs_file_info_new ();
3315 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3316 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3317 /* We put a "safe" default size for going to cache */
3318 *expected_size = (300*1024);
3320 *expected_size = info->size;
3322 gnome_vfs_file_info_unref (info);
3324 stream = tny_vfs_stream_new (handle);
3333 TnyStream *output_stream;
3334 GtkWidget *msg_view;
3339 on_fetch_image_idle_refresh_view (gpointer userdata)
3342 FetchImageData *fidata = (FetchImageData *) userdata;
3344 gdk_threads_enter ();
3345 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3346 ModestMsgViewWindowPrivate *priv;
3348 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3349 priv->fetching_images--;
3350 gtk_widget_queue_draw (fidata->msg_view);
3351 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3353 gdk_threads_leave ();
3355 g_object_unref (fidata->msg_view);
3356 g_object_unref (fidata->window);
3357 g_slice_free (FetchImageData, fidata);
3362 on_fetch_image_thread (gpointer userdata)
3364 FetchImageData *fidata = (FetchImageData *) userdata;
3365 TnyStreamCache *cache;
3366 TnyStream *cache_stream;
3368 cache = modest_runtime_get_images_cache ();
3370 tny_stream_cache_get_stream (cache,
3372 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3373 (gpointer) fidata->uri);
3374 g_free (fidata->cache_id);
3375 g_free (fidata->uri);
3377 if (cache_stream != NULL) {
3380 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3383 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3384 if (G_UNLIKELY (nb_read < 0)) {
3386 } else if (G_LIKELY (nb_read > 0)) {
3387 gssize nb_written = 0;
3389 while (G_UNLIKELY (nb_written < nb_read)) {
3392 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3393 nb_read - nb_written);
3394 if (G_UNLIKELY (len < 0))
3400 tny_stream_close (cache_stream);
3401 g_object_unref (cache_stream);
3404 tny_stream_close (fidata->output_stream);
3405 g_object_unref (fidata->output_stream);
3407 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3413 on_fetch_image (ModestMsgView *msgview,
3416 ModestMsgViewWindow *window)
3418 const gchar *current_account;
3419 ModestMsgViewWindowPrivate *priv;
3420 FetchImageData *fidata;
3422 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3424 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3426 fidata = g_slice_new0 (FetchImageData);
3427 fidata->msg_view = g_object_ref (msgview);
3428 fidata->window = g_object_ref (window);
3429 fidata->uri = g_strdup (uri);
3430 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3431 fidata->output_stream = g_object_ref (stream);
3433 priv->fetching_images++;
3434 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3435 g_object_unref (fidata->output_stream);
3436 g_free (fidata->cache_id);
3437 g_free (fidata->uri);
3438 g_object_unref (fidata->msg_view);
3439 g_slice_free (FetchImageData, fidata);
3440 tny_stream_close (stream);
3441 priv->fetching_images--;
3442 update_progress_hint (window);
3445 update_progress_hint (window);
3451 setup_menu (ModestMsgViewWindow *self)
3453 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3455 /* Settings menu buttons */
3456 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3457 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3458 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3460 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3461 dngettext(GETTEXT_PACKAGE,
3462 "mcen_me_move_message",
3463 "mcen_me_move_messages",
3466 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3467 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3469 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3470 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3471 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3473 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3474 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3475 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3477 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3478 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3479 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3481 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3482 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3483 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3484 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3485 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3486 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3488 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3489 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3490 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3491 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3492 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3493 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3495 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3496 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3497 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3501 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3503 ModestMsgViewWindowPrivate *priv;
3504 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3505 GSList *recipients = NULL;
3507 gboolean contacts_to_add = FALSE;
3509 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3513 header = modest_msg_view_window_get_header (self);
3516 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3517 g_object_unref (header);
3519 recipients = modest_tny_msg_get_all_recipients_list (msg);
3520 g_object_unref (msg);
3523 if (recipients != NULL) {
3524 GtkWidget *picker_dialog;
3525 GtkWidget *selector;
3527 gchar *selected = NULL;
3529 selector = hildon_touch_selector_new_text ();
3530 g_object_ref (selector);
3532 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3533 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3534 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3535 (const gchar *) node->data);
3536 contacts_to_add = TRUE;
3540 if (contacts_to_add) {
3543 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3544 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3546 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3547 HILDON_TOUCH_SELECTOR (selector));
3549 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3551 if (picker_result == GTK_RESPONSE_OK) {
3552 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3554 gtk_widget_destroy (picker_dialog);
3557 modest_address_book_add_address (selected, (GtkWindow *) self);
3562 g_object_unref (selector);
3567 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3571 _modest_msg_view_window_map_event (GtkWidget *widget,
3575 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3577 update_progress_hint (self);
3583 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3585 ModestMsgViewWindowPrivate *priv;
3586 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3588 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3592 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3594 ModestMsgViewWindowPrivate *priv;
3595 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3597 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3599 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3603 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3605 ModestMsgViewWindowPrivate *priv;
3608 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3610 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3611 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3613 if (!message_reader (self, priv, header, NULL, NULL, priv->row_reference)) {
3614 g_warning ("Shouldn't happen, trying to reload a message failed");
3617 g_object_unref (header);
3621 update_branding (ModestMsgViewWindow *self)
3623 const gchar *account;
3624 const gchar *mailbox;
3625 ModestAccountMgr *mgr;
3626 ModestProtocol *protocol = NULL;
3627 gchar *service_name = NULL;
3628 const GdkPixbuf *service_icon = NULL;
3629 ModestMsgViewWindowPrivate *priv;
3631 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3633 account = modest_window_get_active_account (MODEST_WINDOW (self));
3634 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3636 mgr = modest_runtime_get_account_mgr ();
3638 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3639 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3640 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3642 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3643 account, mailbox, MODEST_ICON_SIZE_SMALL);
3647 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3648 g_free (service_name);