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 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
509 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
510 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
512 /* NULL-ize fields if the window is destroyed */
513 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
515 gtk_widget_show_all (GTK_WIDGET(main_vbox));
519 modest_msg_view_window_disconnect_signals (ModestWindow *self)
521 ModestMsgViewWindowPrivate *priv;
522 GtkWidget *header_view = NULL;
523 GtkWindow *parent_window = NULL;
525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
527 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
528 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
529 priv->clipboard_change_handler))
530 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
531 priv->clipboard_change_handler);
533 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
534 priv->queue_change_handler))
535 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
536 priv->queue_change_handler);
538 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
539 priv->account_removed_handler))
540 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
541 priv->account_removed_handler);
543 if (priv->header_model) {
544 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
545 priv->row_changed_handler))
546 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
547 priv->row_changed_handler);
549 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
550 priv->row_deleted_handler))
551 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
552 priv->row_deleted_handler);
554 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
555 priv->row_inserted_handler))
556 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
557 priv->row_inserted_handler);
559 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
560 priv->rows_reordered_handler))
561 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
562 priv->rows_reordered_handler);
565 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
566 priv->sighandlers = NULL;
568 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
569 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
570 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
572 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
573 MODEST_HEADER_VIEW_OBSERVER(self));
579 modest_msg_view_window_finalize (GObject *obj)
581 ModestMsgViewWindowPrivate *priv;
583 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
585 /* Sanity check: shouldn't be needed, the window mgr should
586 call this function before */
587 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
589 if (priv->other_body != NULL) {
590 g_object_unref (priv->other_body);
591 priv->other_body = NULL;
594 if (priv->header_model != NULL) {
595 g_object_unref (priv->header_model);
596 priv->header_model = NULL;
599 if (priv->remove_attachment_banner) {
600 gtk_widget_destroy (priv->remove_attachment_banner);
601 g_object_unref (priv->remove_attachment_banner);
602 priv->remove_attachment_banner = NULL;
605 if (priv->purge_timeout > 0) {
606 g_source_remove (priv->purge_timeout);
607 priv->purge_timeout = 0;
610 if (priv->row_reference) {
611 gtk_tree_row_reference_free (priv->row_reference);
612 priv->row_reference = NULL;
615 if (priv->next_row_reference) {
616 gtk_tree_row_reference_free (priv->next_row_reference);
617 priv->next_row_reference = NULL;
621 g_free (priv->msg_uid);
622 priv->msg_uid = NULL;
625 G_OBJECT_CLASS(parent_class)->finalize (obj);
629 select_next_valid_row (GtkTreeModel *model,
630 GtkTreeRowReference **row_reference,
634 GtkTreeIter tmp_iter;
636 GtkTreePath *next = NULL;
637 gboolean retval = FALSE, finished;
639 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
641 path = gtk_tree_row_reference_get_path (*row_reference);
642 gtk_tree_model_get_iter (model, &tmp_iter, path);
643 gtk_tree_row_reference_free (*row_reference);
644 *row_reference = NULL;
648 TnyHeader *header = NULL;
650 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
651 gtk_tree_model_get (model, &tmp_iter,
652 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
656 if (msg_is_visible (header, is_outbox)) {
657 next = gtk_tree_model_get_path (model, &tmp_iter);
658 *row_reference = gtk_tree_row_reference_new (model, next);
659 gtk_tree_path_free (next);
663 g_object_unref (header);
666 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
667 next = gtk_tree_model_get_path (model, &tmp_iter);
669 /* Ensure that we are not selecting the same */
670 if (gtk_tree_path_compare (path, next) != 0) {
671 gtk_tree_model_get (model, &tmp_iter,
672 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
675 if (msg_is_visible (header, is_outbox)) {
676 *row_reference = gtk_tree_row_reference_new (model, next);
680 g_object_unref (header);
684 /* If we ended up in the same message
685 then there is no valid next
689 gtk_tree_path_free (next);
691 /* If there are no more messages and we don't
692 want to start again in the first one then
693 there is no valid next message */
699 gtk_tree_path_free (path);
704 /* TODO: This should be in _init(), with the parameters as properties. */
706 modest_msg_view_window_construct (ModestMsgViewWindow *self,
707 const gchar *modest_account_name,
708 const gchar *mailbox,
709 const gchar *msg_uid)
712 ModestMsgViewWindowPrivate *priv = NULL;
713 ModestWindowPrivate *parent_priv = NULL;
714 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
715 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
717 obj = G_OBJECT (self);
718 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
719 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
721 priv->msg_uid = g_strdup (msg_uid);
724 parent_priv->menubar = NULL;
726 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
727 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
730 /* Add common dimming rules */
731 modest_dimming_rules_group_add_rules (toolbar_rules_group,
732 modest_msg_view_toolbar_dimming_entries,
733 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
734 MODEST_WINDOW (self));
735 modest_dimming_rules_group_add_rules (clipboard_rules_group,
736 modest_msg_view_clipboard_dimming_entries,
737 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
738 MODEST_WINDOW (self));
740 /* Insert dimming rules group for this window */
741 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
742 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
743 g_object_unref (toolbar_rules_group);
744 g_object_unref (clipboard_rules_group);
746 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
748 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);
749 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
750 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
751 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
752 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
753 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
754 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
755 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
756 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
757 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
758 G_CALLBACK (modest_ui_actions_on_details), obj);
759 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
760 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
761 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
762 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
763 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
764 G_CALLBACK (on_fetch_image), obj);
766 g_signal_connect (G_OBJECT (obj), "key-release-event",
767 G_CALLBACK (modest_msg_view_window_key_event),
770 g_signal_connect (G_OBJECT (obj), "key-press-event",
771 G_CALLBACK (modest_msg_view_window_key_event),
774 g_signal_connect (G_OBJECT (obj), "move-focus",
775 G_CALLBACK (on_move_focus), obj);
777 g_signal_connect (G_OBJECT (obj), "map-event",
778 G_CALLBACK (_modest_msg_view_window_map_event),
781 /* Mail Operation Queue */
782 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
784 G_CALLBACK (on_queue_changed),
787 /* Account manager */
788 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
790 G_CALLBACK(on_account_removed),
793 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
794 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
796 /* First add out toolbar ... */
797 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
799 /* ... and later the find toolbar. This way find toolbar will
800 be shown over the other */
801 priv->find_toolbar = hildon_find_toolbar_new (NULL);
802 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
803 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
804 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
805 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
806 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
807 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
808 priv->last_search = NULL;
810 /* Init the clipboard actions dim status */
811 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
813 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
818 /* FIXME: parameter checks */
820 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
821 const gchar *modest_account_name,
822 const gchar *mailbox,
823 const gchar *msg_uid,
825 GtkTreeRowReference *row_reference)
827 ModestMsgViewWindow *window = NULL;
828 ModestMsgViewWindowPrivate *priv = NULL;
829 TnyFolder *header_folder = NULL;
830 ModestHeaderView *header_view = NULL;
831 ModestWindowMgr *mgr = NULL;
834 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
837 mgr = modest_runtime_get_window_mgr ();
838 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
839 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
841 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
843 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
845 /* Remember the message list's TreeModel so we can detect changes
846 * and change the list selection when necessary: */
847 header_folder = modest_header_view_get_folder (header_view);
849 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
850 TNY_FOLDER_TYPE_OUTBOX);
851 priv->header_folder_id = tny_folder_get_id (header_folder);
852 g_object_unref(header_folder);
855 /* Setup row references and connect signals */
856 priv->header_model = g_object_ref (model);
859 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
860 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
861 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
863 priv->row_reference = NULL;
864 priv->next_row_reference = NULL;
867 /* Connect signals */
868 priv->row_changed_handler =
869 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
870 G_CALLBACK(modest_msg_view_window_on_row_changed),
872 priv->row_deleted_handler =
873 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
874 G_CALLBACK(modest_msg_view_window_on_row_deleted),
876 priv->row_inserted_handler =
877 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
878 G_CALLBACK(modest_msg_view_window_on_row_inserted),
880 priv->rows_reordered_handler =
881 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
882 G_CALLBACK(modest_msg_view_window_on_row_reordered),
885 if (header_view != NULL){
886 modest_header_view_add_observer(header_view,
887 MODEST_HEADER_VIEW_OBSERVER(window));
890 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
891 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
892 update_branding (MODEST_MSG_VIEW_WINDOW (window));
894 /* gtk_widget_show_all (GTK_WIDGET (window)); */
895 modest_msg_view_window_update_priority (window);
896 /* Check dimming rules */
897 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
898 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
899 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
901 return MODEST_WINDOW(window);
905 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
906 const gchar *mailbox,
907 const gchar *msg_uid)
909 ModestMsgViewWindow *window = NULL;
910 ModestMsgViewWindowPrivate *priv = NULL;
911 ModestWindowMgr *mgr = NULL;
913 TnyAccount *account = NULL;
915 mgr = modest_runtime_get_window_mgr ();
916 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
917 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
919 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
921 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
925 is_merge = g_str_has_prefix (msg_uid, "merge:");
927 /* Get the account */
929 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
933 if (is_merge || account) {
934 TnyFolder *folder = NULL;
936 /* Try to get the message, if it's already downloaded
937 we don't need to connect */
939 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
941 ModestTnyAccountStore *account_store;
942 ModestTnyLocalFoldersAccount *local_folders_account;
944 account_store = modest_runtime_get_account_store ();
945 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
946 modest_tny_account_store_get_local_folders_account (account_store));
947 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
948 g_object_unref (local_folders_account);
952 gboolean device_online;
954 device = modest_runtime_get_device();
955 device_online = tny_device_is_online (device);
957 message_reader (window, priv, NULL, msg_uid, folder, NULL);
959 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
961 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
962 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
963 update_branding (MODEST_MSG_VIEW_WINDOW (window));
964 g_object_unref (msg);
966 message_reader (window, priv, NULL, msg_uid, folder, NULL);
969 g_object_unref (folder);
974 /* Check dimming rules */
975 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
976 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
977 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
979 return MODEST_WINDOW(window);
983 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
984 const gchar *modest_account_name,
985 const gchar *mailbox,
986 const gchar *msg_uid,
987 GtkTreeRowReference *row_reference)
989 ModestMsgViewWindow *window = NULL;
990 ModestMsgViewWindowPrivate *priv = NULL;
991 TnyFolder *header_folder = NULL;
992 ModestWindowMgr *mgr = NULL;
996 mgr = modest_runtime_get_window_mgr ();
997 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
998 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1000 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1002 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1004 /* Remember the message list's TreeModel so we can detect changes
1005 * and change the list selection when necessary: */
1007 if (header_view != NULL){
1008 header_folder = modest_header_view_get_folder(header_view);
1009 /* This could happen if the header folder was
1010 unseleted before opening this msg window (for
1011 example if the user selects an account in the
1012 folder view of the main window */
1013 if (header_folder) {
1014 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1015 TNY_FOLDER_TYPE_OUTBOX);
1016 priv->header_folder_id = tny_folder_get_id(header_folder);
1017 g_object_unref(header_folder);
1021 /* Setup row references and connect signals */
1022 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1023 g_object_ref (priv->header_model);
1025 if (row_reference) {
1026 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1027 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1028 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1030 priv->row_reference = NULL;
1031 priv->next_row_reference = NULL;
1034 /* Connect signals */
1035 priv->row_changed_handler =
1036 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1037 G_CALLBACK(modest_msg_view_window_on_row_changed),
1039 priv->row_deleted_handler =
1040 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1041 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1043 priv->row_inserted_handler =
1044 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1045 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1047 priv->rows_reordered_handler =
1048 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1049 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1052 if (header_view != NULL){
1053 modest_header_view_add_observer(header_view,
1054 MODEST_HEADER_VIEW_OBSERVER(window));
1057 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1058 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1060 path = gtk_tree_row_reference_get_path (row_reference);
1061 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1063 gtk_tree_model_get (priv->header_model, &iter,
1064 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1066 message_reader (window, priv, header, NULL, NULL, row_reference);
1067 g_object_unref (header);
1069 gtk_tree_path_free (path);
1071 /* Check dimming rules */
1072 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1073 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1074 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1076 return MODEST_WINDOW(window);
1080 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1081 const gchar *modest_account_name,
1082 const gchar *mailbox,
1083 const gchar *msg_uid)
1085 ModestMsgViewWindow *window = NULL;
1086 ModestMsgViewWindowPrivate *priv = NULL;
1087 ModestWindowMgr *mgr = NULL;
1089 mgr = modest_runtime_get_window_mgr ();
1090 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1091 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1092 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1094 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1096 /* Remember that this is a search result,
1097 * so we can disable some UI appropriately: */
1098 priv->is_search_result = TRUE;
1100 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1101 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1103 update_window_title (window);
1104 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1105 modest_msg_view_window_update_priority (window);
1107 /* Check dimming rules */
1108 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1109 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1110 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1112 return MODEST_WINDOW(window);
1116 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1118 ModestMsgViewWindowPrivate *priv = NULL;
1120 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1121 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1123 return (priv->other_body != NULL);
1127 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1128 TnyMimePart *other_body,
1129 const gchar *modest_account_name,
1130 const gchar *mailbox,
1131 const gchar *msg_uid)
1133 GObject *obj = NULL;
1134 ModestMsgViewWindowPrivate *priv;
1135 ModestWindowMgr *mgr = NULL;
1137 g_return_val_if_fail (msg, NULL);
1138 mgr = modest_runtime_get_window_mgr ();
1139 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1140 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1141 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1142 modest_account_name, mailbox, msg_uid);
1145 priv->other_body = g_object_ref (other_body);
1146 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1148 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1150 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1151 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1153 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1155 /* Check dimming rules */
1156 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1157 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1158 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1160 return MODEST_WINDOW(obj);
1164 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1165 const gchar *modest_account_name,
1166 const gchar *mailbox,
1167 const gchar *msg_uid)
1169 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1173 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1176 ModestMsgViewWindow *window)
1178 check_dimming_rules_after_change (window);
1182 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1184 ModestMsgViewWindow *window)
1186 check_dimming_rules_after_change (window);
1188 /* The window could have dissapeared */
1191 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1193 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1194 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1198 /* On insertions we check if the folder still has the message we are
1199 * showing or do not. If do not, we do nothing. Which means we are still
1200 * not attached to any header folder and thus next/prev buttons are
1201 * still dimmed. Once the message that is shown by msg-view is found, the
1202 * new model of header-view will be attached and the references will be set.
1203 * On each further insertions dimming rules will be checked. However
1204 * this requires extra CPU time at least works.
1205 * (An message might be deleted from TnyFolder and thus will not be
1206 * inserted into the model again for example if it is removed by the
1207 * imap server and the header view is refreshed.)
1210 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1211 GtkTreePath *tree_path,
1212 GtkTreeIter *tree_iter,
1213 ModestMsgViewWindow *window)
1215 ModestMsgViewWindowPrivate *priv = NULL;
1216 TnyHeader *header = NULL;
1218 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1219 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1221 g_assert (model == priv->header_model);
1223 /* Check if the newly inserted message is the same we are actually
1224 * showing. IF not, we should remain detached from the header model
1225 * and thus prev and next toolbar buttons should remain dimmed. */
1226 gtk_tree_model_get (model, tree_iter,
1227 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1230 if (TNY_IS_HEADER (header)) {
1233 uid = modest_tny_folder_get_header_unique_id (header);
1234 if (!g_str_equal(priv->msg_uid, uid)) {
1235 check_dimming_rules_after_change (window);
1237 g_object_unref (G_OBJECT(header));
1241 g_object_unref(G_OBJECT(header));
1244 if (priv->row_reference) {
1245 gtk_tree_row_reference_free (priv->row_reference);
1248 /* Setup row_reference for the actual msg. */
1249 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1250 if (priv->row_reference == NULL) {
1251 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1255 /* Now set up next_row_reference. */
1256 if (priv->next_row_reference) {
1257 gtk_tree_row_reference_free (priv->next_row_reference);
1260 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1261 select_next_valid_row (priv->header_model,
1262 &(priv->next_row_reference), FALSE, priv->is_outbox);
1264 /* Connect the remaining callbacks to become able to detect
1265 * changes in header-view. */
1266 priv->row_changed_handler =
1267 g_signal_connect (priv->header_model, "row-changed",
1268 G_CALLBACK (modest_msg_view_window_on_row_changed),
1270 priv->row_deleted_handler =
1271 g_signal_connect (priv->header_model, "row-deleted",
1272 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1274 priv->rows_reordered_handler =
1275 g_signal_connect (priv->header_model, "rows-reordered",
1276 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1279 check_dimming_rules_after_change (window);
1283 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1287 ModestMsgViewWindow *window)
1289 ModestMsgViewWindowPrivate *priv = NULL;
1290 gboolean already_changed = FALSE;
1292 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1294 /* If the current row was reordered select the proper next
1295 valid row. The same if the next row reference changes */
1296 if (!priv->row_reference ||
1297 !gtk_tree_row_reference_valid (priv->row_reference))
1300 if (priv->next_row_reference &&
1301 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1302 GtkTreePath *cur, *next;
1303 /* Check that the order is still the correct one */
1304 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1305 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1306 gtk_tree_path_next (cur);
1307 if (gtk_tree_path_compare (cur, next) != 0) {
1308 gtk_tree_row_reference_free (priv->next_row_reference);
1309 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1310 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1311 already_changed = TRUE;
1313 gtk_tree_path_free (cur);
1314 gtk_tree_path_free (next);
1316 if (priv->next_row_reference)
1317 gtk_tree_row_reference_free (priv->next_row_reference);
1318 /* Update next row reference */
1319 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1320 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1321 already_changed = TRUE;
1324 check_dimming_rules_after_change (window);
1327 /* The modest_msg_view_window_update_model_replaced implements update
1328 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1329 * actually belongs to the header-view is the same as the TnyFolder of
1330 * the message of msg-view or not. If they are different, there is
1331 * nothing to do. If they are the same, then the model has replaced and
1332 * the reference in msg-view shall be replaced from the old model to
1333 * the new model. In this case the view will be detached from it's
1334 * header folder. From this point the next/prev buttons are dimmed.
1337 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1338 GtkTreeModel *model,
1339 const gchar *tny_folder_id)
1341 ModestMsgViewWindowPrivate *priv = NULL;
1342 ModestMsgViewWindow *window = NULL;
1344 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1345 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1347 window = MODEST_MSG_VIEW_WINDOW(observer);
1348 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1350 /* If there is an other folder in the header-view then we do
1351 * not care about it's model (msg list). Else if the
1352 * header-view shows the folder the msg shown by us is in, we
1353 * shall replace our model reference and make some check. */
1354 if(model == NULL || tny_folder_id == NULL ||
1355 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1358 /* Model is changed(replaced), so we should forget the old
1359 * one. Because there might be other references and there
1360 * might be some change on the model even if we unreferenced
1361 * it, we need to disconnect our signals here. */
1362 if (priv->header_model) {
1363 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1364 priv->row_changed_handler))
1365 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1366 priv->row_changed_handler);
1367 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1368 priv->row_deleted_handler))
1369 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1370 priv->row_deleted_handler);
1371 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1372 priv->row_inserted_handler))
1373 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1374 priv->row_inserted_handler);
1375 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1376 priv->rows_reordered_handler))
1377 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1378 priv->rows_reordered_handler);
1381 if (priv->row_reference)
1382 gtk_tree_row_reference_free (priv->row_reference);
1383 if (priv->next_row_reference)
1384 gtk_tree_row_reference_free (priv->next_row_reference);
1385 g_object_unref(priv->header_model);
1388 priv->row_changed_handler = 0;
1389 priv->row_deleted_handler = 0;
1390 priv->row_inserted_handler = 0;
1391 priv->rows_reordered_handler = 0;
1392 priv->next_row_reference = NULL;
1393 priv->row_reference = NULL;
1394 priv->header_model = NULL;
1397 priv->header_model = g_object_ref (model);
1399 /* Also we must connect to the new model for row insertions.
1400 * Only for insertions now. We will need other ones only after
1401 * the msg is show by msg-view is added to the new model. */
1402 priv->row_inserted_handler =
1403 g_signal_connect (priv->header_model, "row-inserted",
1404 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1407 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1408 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1412 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1414 ModestMsgViewWindowPrivate *priv= NULL;
1416 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1417 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1419 return priv->progress_hint;
1423 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1425 ModestMsgViewWindowPrivate *priv= NULL;
1427 TnyHeader *header = NULL;
1428 GtkTreePath *path = NULL;
1431 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1432 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1434 /* If the message was not obtained from a treemodel,
1435 * for instance if it was opened directly by the search UI:
1437 if (priv->header_model == NULL ||
1438 priv->row_reference == NULL ||
1439 !gtk_tree_row_reference_valid (priv->row_reference)) {
1440 msg = modest_msg_view_window_get_message (self);
1442 header = tny_msg_get_header (msg);
1443 g_object_unref (msg);
1448 /* Get iter of the currently selected message in the header view: */
1449 path = gtk_tree_row_reference_get_path (priv->row_reference);
1450 g_return_val_if_fail (path != NULL, NULL);
1451 gtk_tree_model_get_iter (priv->header_model,
1455 /* Get current message header */
1456 gtk_tree_model_get (priv->header_model, &iter,
1457 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1460 gtk_tree_path_free (path);
1465 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1467 ModestMsgViewWindowPrivate *priv;
1469 g_return_val_if_fail (self, NULL);
1471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1473 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1477 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1479 ModestMsgViewWindowPrivate *priv;
1481 g_return_val_if_fail (self, NULL);
1483 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1485 return (const gchar*) priv->msg_uid;
1488 /* Used for the Ctrl+F accelerator */
1490 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1493 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1494 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1496 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1497 modest_msg_view_window_find_toolbar_close (obj, data);
1499 modest_msg_view_window_show_find_toolbar (obj, data);
1503 /* Handler for menu option */
1505 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1508 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1509 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1511 gtk_widget_show (priv->find_toolbar);
1512 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1515 /* Handler for click on the "X" close button in find toolbar */
1517 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1518 ModestMsgViewWindow *obj)
1520 ModestMsgViewWindowPrivate *priv;
1522 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1525 gtk_widget_hide (priv->find_toolbar);
1526 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1530 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1531 ModestMsgViewWindow *obj)
1533 gchar *current_search;
1534 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1536 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1537 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1541 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1543 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1544 g_free (current_search);
1545 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1549 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1551 g_free (priv->last_search);
1552 priv->last_search = g_strdup (current_search);
1553 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1556 hildon_banner_show_information (NULL, NULL,
1557 _HL("ckct_ib_find_no_matches"));
1558 g_free (priv->last_search);
1559 priv->last_search = NULL;
1561 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1564 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1565 hildon_banner_show_information (NULL, NULL,
1566 _HL("ckct_ib_find_search_complete"));
1567 g_free (priv->last_search);
1568 priv->last_search = NULL;
1570 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1574 g_free (current_search);
1579 modest_msg_view_window_set_zoom (ModestWindow *window,
1582 ModestMsgViewWindowPrivate *priv;
1583 ModestWindowPrivate *parent_priv;
1585 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1587 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1588 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1589 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1594 modest_msg_view_window_get_zoom (ModestWindow *window)
1596 ModestMsgViewWindowPrivate *priv;
1598 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1600 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1601 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1605 modest_msg_view_window_zoom_plus (ModestWindow *window)
1608 ModestMsgViewWindowPrivate *priv;
1612 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1613 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1615 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1617 if (zoom_level >= 2.0) {
1618 hildon_banner_show_information (NULL, NULL,
1619 _CS("ckct_ib_max_zoom_level_reached"));
1621 } else if (zoom_level >= 1.5) {
1623 } else if (zoom_level >= 1.2) {
1625 } else if (zoom_level >= 1.0) {
1627 } else if (zoom_level >= 0.8) {
1629 } else if (zoom_level >= 0.5) {
1635 /* set zoom level */
1636 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1637 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1638 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1639 g_free (banner_text);
1640 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1646 modest_msg_view_window_zoom_minus (ModestWindow *window)
1649 ModestMsgViewWindowPrivate *priv;
1653 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1654 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1656 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1658 if (zoom_level <= 0.5) {
1659 hildon_banner_show_information (NULL, NULL,
1660 _CS("ckct_ib_min_zoom_level_reached"));
1662 } else if (zoom_level <= 0.8) {
1664 } else if (zoom_level <= 1.0) {
1666 } else if (zoom_level <= 1.2) {
1668 } else if (zoom_level <= 1.5) {
1670 } else if (zoom_level <= 2.0) {
1676 /* set zoom level */
1677 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1678 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1679 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1680 g_free (banner_text);
1681 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1688 modest_msg_view_window_key_event (GtkWidget *window,
1694 focus = gtk_window_get_focus (GTK_WINDOW (window));
1696 /* for the find toolbar case */
1697 if (focus && GTK_IS_ENTRY (focus)) {
1698 if (event->keyval == GDK_BackSpace) {
1700 copy = gdk_event_copy ((GdkEvent *) event);
1701 gtk_widget_event (focus, copy);
1702 gdk_event_free (copy);
1707 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1708 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1709 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1710 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1711 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1712 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1713 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1714 /* gboolean return_value; */
1716 if (event->type == GDK_KEY_PRESS) {
1717 GtkScrollType scroll_type;
1719 switch (event->keyval) {
1722 scroll_type = GTK_SCROLL_STEP_UP; break;
1725 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1727 case GDK_KP_Page_Up:
1728 scroll_type = GTK_SCROLL_PAGE_UP; break;
1730 case GDK_KP_Page_Down:
1731 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1734 scroll_type = GTK_SCROLL_START; break;
1737 scroll_type = GTK_SCROLL_END; break;
1738 default: scroll_type = GTK_SCROLL_NONE;
1741 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1742 /* scroll_type, FALSE, &return_value); */
1753 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1756 ModestMsgViewWindowPrivate *priv;
1757 GtkTreeIter tmp_iter;
1758 gboolean is_last_selected;
1760 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1761 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1763 /*if no model (so no rows at all), then virtually we are the last*/
1764 if (!priv->header_model || !priv->row_reference)
1767 if (!gtk_tree_row_reference_valid (priv->row_reference))
1770 path = gtk_tree_row_reference_get_path (priv->row_reference);
1774 is_last_selected = TRUE;
1775 while (is_last_selected) {
1777 gtk_tree_path_next (path);
1778 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1780 gtk_tree_model_get (priv->header_model, &tmp_iter,
1781 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1784 if (msg_is_visible (header, priv->is_outbox))
1785 is_last_selected = FALSE;
1786 g_object_unref(G_OBJECT(header));
1789 gtk_tree_path_free (path);
1790 return is_last_selected;
1794 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1796 ModestMsgViewWindowPrivate *priv;
1798 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1799 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1801 return priv->header_model != NULL;
1805 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1807 ModestMsgViewWindowPrivate *priv;
1809 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1810 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1812 return priv->is_search_result;
1816 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1818 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1820 if (!check_outbox) {
1823 ModestTnySendQueueStatus status;
1824 status = modest_tny_all_send_queues_get_msg_status (header);
1825 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1826 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1831 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1834 ModestMsgViewWindowPrivate *priv;
1835 gboolean is_first_selected;
1836 GtkTreeIter tmp_iter;
1838 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1839 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1841 /*if no model (so no rows at all), then virtually we are the first*/
1842 if (!priv->header_model || !priv->row_reference)
1845 if (!gtk_tree_row_reference_valid (priv->row_reference))
1848 path = gtk_tree_row_reference_get_path (priv->row_reference);
1852 is_first_selected = TRUE;
1853 while (is_first_selected) {
1855 if(!gtk_tree_path_prev (path))
1857 /* Here the 'if' is needless for logic, but let make sure
1858 * iter is valid for gtk_tree_model_get. */
1859 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1861 gtk_tree_model_get (priv->header_model, &tmp_iter,
1862 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1865 if (msg_is_visible (header, priv->is_outbox))
1866 is_first_selected = FALSE;
1867 g_object_unref(G_OBJECT(header));
1870 gtk_tree_path_free (path);
1871 return is_first_selected;
1878 GtkTreeRowReference *row_reference;
1882 message_reader_performer (gboolean canceled,
1884 GtkWindow *parent_window,
1885 TnyAccount *account,
1888 ModestMailOperation *mail_op = NULL;
1889 MsgReaderInfo *info;
1891 info = (MsgReaderInfo *) user_data;
1892 if (canceled || err) {
1893 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1897 /* Register the header - it'll be unregistered in the callback */
1899 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1901 /* New mail operation */
1902 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1903 modest_ui_actions_disk_operations_error_handler,
1906 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1908 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1910 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1911 g_object_unref (mail_op);
1913 /* Update dimming rules */
1914 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1915 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1918 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1919 g_free (info->msg_uid);
1921 g_object_unref (info->folder);
1923 g_object_unref (info->header);
1924 g_slice_free (MsgReaderInfo, info);
1929 * Reads the message whose summary item is @header. It takes care of
1930 * several things, among others:
1932 * If the message was not previously downloaded then ask the user
1933 * before downloading. If there is no connection launch the connection
1934 * dialog. Update toolbar dimming rules.
1936 * Returns: TRUE if the mail operation was started, otherwise if the
1937 * user do not want to download the message, or if the user do not
1938 * want to connect, then the operation is not issued
1941 message_reader (ModestMsgViewWindow *window,
1942 ModestMsgViewWindowPrivate *priv,
1944 const gchar *msg_uid,
1946 GtkTreeRowReference *row_reference)
1948 ModestWindowMgr *mgr;
1949 TnyAccount *account;
1950 MsgReaderInfo *info;
1952 /* We set the header from model while we're loading */
1953 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1954 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1960 g_object_ref (folder);
1962 mgr = modest_runtime_get_window_mgr ();
1963 /* Msg download completed */
1964 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1966 /* Ask the user if he wants to download the message if
1968 if (!tny_device_is_online (modest_runtime_get_device())) {
1969 GtkResponseType response;
1971 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1972 _("mcen_nc_get_msg"));
1973 if (response == GTK_RESPONSE_CANCEL) {
1974 update_window_title (window);
1979 folder = tny_header_get_folder (header);
1981 info = g_slice_new (MsgReaderInfo);
1982 info->msg_uid = g_strdup (msg_uid);
1984 info->header = g_object_ref (header);
1986 info->header = NULL;
1988 info->folder = g_object_ref (folder);
1990 info->folder = NULL;
1991 if (row_reference) {
1992 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1994 info->row_reference = NULL;
1997 /* Offer the connection dialog if necessary */
1998 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2000 TNY_FOLDER_STORE (folder),
2001 message_reader_performer,
2003 g_object_unref (folder);
2009 folder = tny_header_get_folder (header);
2011 account = tny_folder_get_account (folder);
2012 info = g_slice_new (MsgReaderInfo);
2013 info->msg_uid = g_strdup (msg_uid);
2015 info->folder = g_object_ref (folder);
2017 info->folder = NULL;
2019 info->header = g_object_ref (header);
2021 info->header = NULL;
2023 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2025 info->row_reference = NULL;
2027 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2028 g_object_unref (account);
2030 g_object_unref (folder);
2036 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2038 ModestMsgViewWindowPrivate *priv;
2039 GtkTreePath *path= NULL;
2040 GtkTreeIter tmp_iter;
2042 gboolean retval = TRUE;
2043 GtkTreeRowReference *row_reference = NULL;
2045 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2046 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2048 if (!priv->row_reference)
2051 /* Update the next row reference if it's not valid. This could
2052 happen if for example the header which it was pointing to,
2053 was deleted. The best place to do it is in the row-deleted
2054 handler but the tinymail model do not work like the glib
2055 tree models and reports the deletion when the row is still
2057 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2058 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2059 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2060 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2063 if (priv->next_row_reference)
2064 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2068 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2070 gtk_tree_model_get_iter (priv->header_model,
2073 gtk_tree_path_free (path);
2075 gtk_tree_model_get (priv->header_model, &tmp_iter,
2076 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2079 /* Read the message & show it */
2080 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2083 gtk_tree_row_reference_free (row_reference);
2086 g_object_unref (header);
2092 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2094 ModestMsgViewWindowPrivate *priv = NULL;
2096 gboolean finished = FALSE;
2097 gboolean retval = FALSE;
2099 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2100 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2102 /* Return inmediatly if there is no header model */
2103 if (!priv->header_model || !priv->row_reference)
2106 path = gtk_tree_row_reference_get_path (priv->row_reference);
2107 while (!finished && gtk_tree_path_prev (path)) {
2111 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2112 gtk_tree_model_get (priv->header_model, &iter,
2113 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2117 if (msg_is_visible (header, priv->is_outbox)) {
2118 GtkTreeRowReference *row_reference;
2119 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2120 /* Read the message & show it */
2121 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2122 gtk_tree_row_reference_free (row_reference);
2126 g_object_unref (header);
2130 gtk_tree_path_free (path);
2135 view_msg_cb (ModestMailOperation *mail_op,
2142 ModestMsgViewWindow *self = NULL;
2143 ModestMsgViewWindowPrivate *priv = NULL;
2144 GtkTreeRowReference *row_reference = NULL;
2146 /* Unregister the header (it was registered before creating the mail operation) */
2147 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2149 row_reference = (GtkTreeRowReference *) user_data;
2152 gtk_tree_row_reference_free (row_reference);
2153 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2155 /* Restore window title */
2156 update_window_title (self);
2157 g_object_unref (self);
2162 /* If there was any error */
2163 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2165 gtk_tree_row_reference_free (row_reference);
2166 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2168 /* Restore window title */
2169 update_window_title (self);
2170 g_object_unref (self);
2175 /* Get the window */
2176 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2177 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2178 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2180 /* Update the row reference */
2181 if (priv->row_reference != NULL) {
2182 gtk_tree_row_reference_free (priv->row_reference);
2183 priv->row_reference = row_reference?gtk_tree_row_reference_copy (row_reference):NULL;
2184 if (priv->next_row_reference != NULL) {
2185 gtk_tree_row_reference_free (priv->next_row_reference);
2187 if (row_reference) {
2188 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2189 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2191 priv->next_row_reference = NULL;
2195 /* Mark header as read */
2196 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2197 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2199 /* Set new message */
2200 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2201 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2202 modest_msg_view_window_update_priority (self);
2203 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2204 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2205 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2208 /* Set the new message uid of the window */
2209 if (priv->msg_uid) {
2210 g_free (priv->msg_uid);
2211 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2214 /* Notify the observers */
2215 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2216 0, priv->header_model, priv->row_reference);
2219 g_object_unref (self);
2221 gtk_tree_row_reference_free (row_reference);
2225 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2227 ModestMsgViewWindowPrivate *priv;
2229 TnyFolderType folder_type;
2231 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2233 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2235 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2239 folder = tny_msg_get_folder (msg);
2241 folder_type = modest_tny_folder_guess_folder_type (folder);
2242 g_object_unref (folder);
2244 g_object_unref (msg);
2252 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2254 ModestMsgViewWindowPrivate *priv;
2255 TnyHeader *header = NULL;
2256 TnyHeaderFlags flags = 0;
2258 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2260 if (priv->header_model && priv->row_reference) {
2262 GtkTreePath *path = NULL;
2264 path = gtk_tree_row_reference_get_path (priv->row_reference);
2265 g_return_if_fail (path != NULL);
2266 gtk_tree_model_get_iter (priv->header_model,
2268 gtk_tree_row_reference_get_path (priv->row_reference));
2270 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2272 gtk_tree_path_free (path);
2275 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2277 header = tny_msg_get_header (msg);
2278 g_object_unref (msg);
2283 flags = tny_header_get_flags (header);
2284 g_object_unref(G_OBJECT(header));
2287 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2292 toolbar_resize (ModestMsgViewWindow *self)
2294 ModestMsgViewWindowPrivate *priv = NULL;
2295 ModestWindowPrivate *parent_priv = NULL;
2297 gint static_button_size;
2298 ModestWindowMgr *mgr;
2300 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2301 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2302 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2304 mgr = modest_runtime_get_window_mgr ();
2305 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2307 if (parent_priv->toolbar) {
2308 /* left size buttons */
2309 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2310 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2311 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2312 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2313 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2314 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2315 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2316 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2317 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2318 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2319 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2320 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2321 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2322 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2323 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2324 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2326 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2327 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2328 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2329 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2334 modest_msg_view_window_show_toolbar (ModestWindow *self,
2335 gboolean show_toolbar)
2337 ModestMsgViewWindowPrivate *priv = NULL;
2338 ModestWindowPrivate *parent_priv;
2340 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2341 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2343 /* Set optimized view status */
2344 priv->optimized_view = !show_toolbar;
2346 if (!parent_priv->toolbar) {
2347 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2349 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2350 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2352 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2353 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2354 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2357 hildon_window_add_toolbar (HILDON_WINDOW (self),
2358 GTK_TOOLBAR (parent_priv->toolbar));
2363 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2364 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2365 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2367 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2368 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2369 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2371 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2374 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2375 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2380 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2382 ModestMsgViewWindow *window)
2384 if (!GTK_WIDGET_VISIBLE (window))
2387 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2391 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2393 ModestMsgViewWindowPrivate *priv;
2395 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2396 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2398 return priv->progress_hint;
2402 observers_empty (ModestMsgViewWindow *self)
2405 ModestMsgViewWindowPrivate *priv;
2406 gboolean is_empty = TRUE;
2407 guint pending_ops = 0;
2409 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2410 tmp = priv->progress_widgets;
2412 /* Check all observers */
2413 while (tmp && is_empty) {
2414 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2415 is_empty = pending_ops == 0;
2417 tmp = g_slist_next(tmp);
2424 on_account_removed (TnyAccountStore *account_store,
2425 TnyAccount *account,
2428 /* Do nothing if it's a transport account, because we only
2429 show the messages of a store account */
2430 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2431 const gchar *parent_acc = NULL;
2432 const gchar *our_acc = NULL;
2434 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2435 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2437 /* Close this window if I'm showing a message of the removed account */
2438 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2439 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2444 on_mail_operation_started (ModestMailOperation *mail_op,
2447 ModestMsgViewWindow *self;
2448 ModestMailOperationTypeOperation op_type;
2450 ModestMsgViewWindowPrivate *priv;
2451 GObject *source = NULL;
2453 self = MODEST_MSG_VIEW_WINDOW (user_data);
2454 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2455 op_type = modest_mail_operation_get_type_operation (mail_op);
2456 tmp = priv->progress_widgets;
2457 source = modest_mail_operation_get_source(mail_op);
2458 if (G_OBJECT (self) == source) {
2459 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2460 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2461 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2462 set_progress_hint (self, TRUE);
2464 modest_progress_object_add_operation (
2465 MODEST_PROGRESS_OBJECT (tmp->data),
2467 tmp = g_slist_next (tmp);
2471 g_object_unref (source);
2473 /* Update dimming rules */
2474 check_dimming_rules_after_change (self);
2478 on_mail_operation_finished (ModestMailOperation *mail_op,
2481 ModestMsgViewWindow *self;
2482 ModestMailOperationTypeOperation op_type;
2484 ModestMsgViewWindowPrivate *priv;
2486 self = MODEST_MSG_VIEW_WINDOW (user_data);
2487 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2488 op_type = modest_mail_operation_get_type_operation (mail_op);
2489 tmp = priv->progress_widgets;
2491 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2492 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2493 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2495 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2497 tmp = g_slist_next (tmp);
2500 /* If no more operations are being observed, NORMAL mode is enabled again */
2501 if (observers_empty (self)) {
2502 set_progress_hint (self, FALSE);
2506 /* Update dimming rules. We have to do this right here
2507 and not in view_msg_cb because at that point the
2508 transfer mode is still enabled so the dimming rule
2509 won't let the user delete the message that has been
2510 readed for example */
2511 check_dimming_rules_after_change (self);
2515 on_queue_changed (ModestMailOperationQueue *queue,
2516 ModestMailOperation *mail_op,
2517 ModestMailOperationQueueNotification type,
2518 ModestMsgViewWindow *self)
2520 ModestMsgViewWindowPrivate *priv;
2522 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2524 /* If this operations was created by another window, do nothing */
2525 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2528 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2529 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2531 "operation-started",
2532 G_CALLBACK (on_mail_operation_started),
2534 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2536 "operation-finished",
2537 G_CALLBACK (on_mail_operation_finished),
2539 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2540 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2542 "operation-started");
2543 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2545 "operation-finished");
2550 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2552 ModestMsgViewWindowPrivate *priv;
2553 TnyList *selected_attachments = NULL;
2555 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2556 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2558 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2559 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2561 return selected_attachments;
2565 ModestMsgViewWindow *self;
2567 } DecodeAsyncHelper;
2570 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2576 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2578 /* It could happen that the window was closed */
2579 if (GTK_WIDGET_VISIBLE (helper->self))
2580 set_progress_hint (helper->self, FALSE);
2582 if (cancelled || err) {
2584 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2585 modest_platform_information_banner (NULL, NULL, msg);
2591 /* make the file read-only */
2592 g_chmod(helper->file_path, 0444);
2594 /* Activate the file */
2595 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2599 g_object_unref (helper->self);
2600 g_free (helper->file_path);
2601 g_slice_free (DecodeAsyncHelper, helper);
2605 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2606 TnyMimePart *mime_part)
2608 ModestMsgViewWindowPrivate *priv;
2609 const gchar *msg_uid;
2610 gchar *attachment_uid = NULL;
2611 gint attachment_index = 0;
2612 TnyList *attachments;
2614 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2615 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2616 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2618 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2619 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2620 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2621 g_object_unref (attachments);
2623 if (msg_uid && attachment_index >= 0) {
2624 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2627 if (mime_part == NULL) {
2628 gboolean error = FALSE;
2629 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2630 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2632 } else if (tny_list_get_length (selected_attachments) > 1) {
2633 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2637 iter = tny_list_create_iterator (selected_attachments);
2638 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2639 g_object_unref (iter);
2641 if (selected_attachments)
2642 g_object_unref (selected_attachments);
2647 g_object_ref (mime_part);
2650 if (tny_mime_part_is_purged (mime_part))
2653 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2654 gchar *filepath = NULL;
2655 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2656 gboolean show_error_banner = FALSE;
2657 TnyFsStream *temp_stream = NULL;
2658 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2661 if (temp_stream != NULL) {
2662 DecodeAsyncHelper *helper;
2664 /* Activate progress hint */
2665 set_progress_hint (window, TRUE);
2667 helper = g_slice_new0 (DecodeAsyncHelper);
2668 helper->self = g_object_ref (window);
2669 helper->file_path = g_strdup (filepath);
2671 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2672 on_decode_to_stream_async_handler,
2675 g_object_unref (temp_stream);
2676 /* NOTE: files in the temporary area will be automatically
2677 * cleaned after some time if they are no longer in use */
2680 const gchar *content_type;
2681 /* the file may already exist but it isn't writable,
2682 * let's try to open it anyway */
2683 content_type = tny_mime_part_get_content_type (mime_part);
2684 modest_platform_activate_file (filepath, content_type);
2686 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2687 show_error_banner = TRUE;
2692 if (show_error_banner)
2693 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2694 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2695 ModestWindowMgr *mgr;
2696 ModestWindow *msg_win = NULL;
2697 TnyMsg *current_msg;
2701 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2702 mgr = modest_runtime_get_window_mgr ();
2703 header = tny_msg_get_header (TNY_MSG (current_msg));
2704 found = modest_window_mgr_find_registered_message_uid (mgr,
2709 g_debug ("window for this body is already being created");
2712 /* it's not found, so create a new window for it */
2713 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2714 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2715 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2717 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2719 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2720 account, mailbox, attachment_uid);
2722 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2723 modest_window_get_zoom (MODEST_WINDOW (window)));
2724 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2725 gtk_widget_show_all (GTK_WIDGET (msg_win));
2727 gtk_widget_destroy (GTK_WIDGET (msg_win));
2729 g_object_unref (current_msg);
2731 /* message attachment */
2732 TnyHeader *header = NULL;
2733 ModestWindowMgr *mgr;
2734 ModestWindow *msg_win = NULL;
2737 header = tny_msg_get_header (TNY_MSG (mime_part));
2738 mgr = modest_runtime_get_window_mgr ();
2739 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2742 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2743 * thus, we don't do anything */
2744 g_debug ("window for is already being created");
2746 /* it's not found, so create a new window for it */
2747 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2748 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2749 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2751 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2752 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2753 mailbox, attachment_uid);
2754 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2755 modest_window_get_zoom (MODEST_WINDOW (window)));
2756 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2757 gtk_widget_show_all (GTK_WIDGET (msg_win));
2759 gtk_widget_destroy (GTK_WIDGET (msg_win));
2765 g_free (attachment_uid);
2767 g_object_unref (mime_part);
2779 GnomeVFSResult result;
2783 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2784 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2785 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2786 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2789 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2793 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2794 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2795 g_free (pair->filename);
2796 g_object_unref (pair->part);
2797 g_slice_free (SaveMimePartPair, pair);
2799 g_list_free (info->pairs);
2803 g_slice_free (SaveMimePartInfo, info);
2808 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2810 /* This is a GDK lock because we are an idle callback and
2811 * hildon_banner_show_information is or does Gtk+ code */
2813 gdk_threads_enter (); /* CHECKED */
2814 if (info->result == GNOME_VFS_OK) {
2815 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2816 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2819 /* Check if the uri belongs to the external mmc */
2820 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2821 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2823 msg = g_strdup (_KR("cerm_memory_card_full"));
2824 modest_platform_information_banner (NULL, NULL, msg);
2827 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2829 save_mime_part_info_free (info, FALSE);
2830 gdk_threads_leave (); /* CHECKED */
2836 save_mime_part_to_file (SaveMimePartInfo *info)
2838 GnomeVFSHandle *handle;
2840 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2842 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2843 if (info->result == GNOME_VFS_OK) {
2844 GError *error = NULL;
2845 stream = tny_vfs_stream_new (handle);
2846 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2847 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2849 if ((error->domain == TNY_ERROR_DOMAIN) &&
2850 (error->code == TNY_IO_ERROR_WRITE) &&
2851 (errno == ENOSPC)) {
2852 info->result = GNOME_VFS_ERROR_NO_SPACE;
2854 info->result = GNOME_VFS_ERROR_IO;
2857 g_object_unref (G_OBJECT (stream));
2859 g_warning ("Could not create save attachment %s: %s\n",
2860 pair->filename, gnome_vfs_result_to_string (info->result));
2863 /* Go on saving remaining files */
2864 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2865 if (info->pairs != NULL) {
2866 save_mime_part_to_file (info);
2868 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2875 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2876 SaveMimePartInfo *info)
2878 gboolean is_ok = TRUE;
2879 gint replaced_files = 0;
2880 const GList *files = info->pairs;
2881 const GList *iter, *to_replace = NULL;
2883 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2884 SaveMimePartPair *pair = iter->data;
2885 if (modest_utils_file_exists (pair->filename)) {
2887 if (replaced_files == 1)
2891 if (replaced_files) {
2894 if (replaced_files == 1) {
2895 SaveMimePartPair *pair = to_replace->data;
2896 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2897 gchar *escaped_basename, *message;
2899 escaped_basename = g_uri_unescape_string (basename, NULL);
2900 message = g_strdup_printf ("%s\n%s",
2901 _FM("docm_nc_replace_file"),
2902 (escaped_basename) ? escaped_basename : "");
2903 response = modest_platform_run_confirmation_dialog (parent, message);
2905 g_free (escaped_basename);
2907 response = modest_platform_run_confirmation_dialog (parent,
2908 _FM("docm_nc_replace_multiple"));
2910 if (response != GTK_RESPONSE_OK)
2915 save_mime_part_info_free (info, TRUE);
2917 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2923 save_attachments_response (GtkDialog *dialog,
2927 TnyList *mime_parts;
2929 GList *files_to_save = NULL;
2930 gchar *current_folder;
2932 mime_parts = TNY_LIST (user_data);
2934 if (arg1 != GTK_RESPONSE_OK)
2937 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2938 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2939 if (current_folder && *current_folder != '\0') {
2941 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2942 current_folder,&err);
2944 g_debug ("Error storing latest used folder: %s", err->message);
2948 g_free (current_folder);
2950 if (!modest_utils_folder_writable (chooser_uri)) {
2951 const gchar *err_msg;
2953 #ifdef MODEST_PLATFORM_MAEMO
2954 if (modest_maemo_utils_in_usb_mode ()) {
2955 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
2957 err_msg = _FM("sfil_ib_readonly_location");
2960 err_msg = _FM("sfil_ib_readonly_location");
2962 hildon_banner_show_information (NULL, NULL, err_msg);
2966 iter = tny_list_create_iterator (mime_parts);
2967 while (!tny_iterator_is_done (iter)) {
2968 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2970 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2971 !tny_mime_part_is_purged (mime_part) &&
2972 (tny_mime_part_get_filename (mime_part) != NULL)) {
2973 SaveMimePartPair *pair;
2975 pair = g_slice_new0 (SaveMimePartPair);
2977 if (tny_list_get_length (mime_parts) > 1) {
2979 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2980 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2983 pair->filename = g_strdup (chooser_uri);
2985 pair->part = mime_part;
2986 files_to_save = g_list_prepend (files_to_save, pair);
2988 tny_iterator_next (iter);
2990 g_object_unref (iter);
2993 if (files_to_save != NULL) {
2994 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2995 info->pairs = files_to_save;
2996 info->result = TRUE;
2997 info->uri = g_strdup (chooser_uri);
2998 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3000 g_free (chooser_uri);
3003 /* Free and close the dialog */
3004 g_object_unref (mime_parts);
3005 gtk_widget_destroy (GTK_WIDGET (dialog));
3009 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3010 TnyList *mime_parts)
3012 ModestMsgViewWindowPrivate *priv;
3013 GtkWidget *save_dialog = NULL;
3014 gchar *conf_folder = NULL;
3015 gchar *filename = NULL;
3016 gchar *save_multiple_str = NULL;
3017 const gchar *root_folder = "file:///";
3019 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3020 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3022 if (mime_parts == NULL) {
3023 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3024 * selection available */
3025 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3026 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3027 g_object_unref (mime_parts);
3030 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3032 g_object_unref (mime_parts);
3038 g_object_ref (mime_parts);
3041 /* prepare dialog */
3042 if (tny_list_get_length (mime_parts) == 1) {
3044 /* only one attachment selected */
3045 iter = tny_list_create_iterator (mime_parts);
3046 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3047 g_object_unref (iter);
3048 if (!modest_tny_mime_part_is_msg (mime_part) &&
3049 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3050 !tny_mime_part_is_purged (mime_part)) {
3051 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3053 /* TODO: show any error? */
3054 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3055 g_object_unref (mime_parts);
3058 g_object_unref (mime_part);
3060 gint num = tny_list_get_length (mime_parts);
3061 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3062 "sfil_va_number_of_objects_attachment",
3063 "sfil_va_number_of_objects_attachments",
3067 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3068 GTK_FILE_CHOOSER_ACTION_SAVE);
3070 /* Get last used folder */
3071 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3073 /* File chooser stops working if we select "file:///" as current folder */
3074 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3075 g_free (conf_folder);
3079 if (conf_folder && conf_folder[0] != '\0') {
3080 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3083 /* Set the default folder to images folder */
3084 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3085 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3086 g_free (docs_folder);
3088 g_free (conf_folder);
3092 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3097 /* if multiple, set multiple string */
3098 if (save_multiple_str) {
3099 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3100 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3103 /* We must run this asynchronously, because the hildon dialog
3104 performs a gtk_dialog_run by itself which leads to gdk
3106 g_signal_connect (save_dialog, "response",
3107 G_CALLBACK (save_attachments_response), mime_parts);
3109 gtk_widget_show_all (save_dialog);
3113 show_remove_attachment_information (gpointer userdata)
3115 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3116 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3118 /* We're outside the main lock */
3119 gdk_threads_enter ();
3121 if (priv->remove_attachment_banner != NULL) {
3122 gtk_widget_destroy (priv->remove_attachment_banner);
3123 g_object_unref (priv->remove_attachment_banner);
3126 priv->remove_attachment_banner = g_object_ref (
3127 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3129 gdk_threads_leave ();
3135 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3137 ModestMsgViewWindowPrivate *priv;
3138 TnyList *mime_parts = NULL, *tmp;
3139 gchar *confirmation_message;
3145 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3146 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3148 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3149 * because we don't have selection
3151 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3153 /* Remove already purged messages from mime parts list. We use
3154 a copy of the list to remove items in the original one */
3155 tmp = tny_list_copy (mime_parts);
3156 iter = tny_list_create_iterator (tmp);
3157 while (!tny_iterator_is_done (iter)) {
3158 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3159 if (tny_mime_part_is_purged (part))
3160 tny_list_remove (mime_parts, (GObject *) part);
3162 g_object_unref (part);
3163 tny_iterator_next (iter);
3165 g_object_unref (tmp);
3166 g_object_unref (iter);
3168 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3169 tny_list_get_length (mime_parts) == 0) {
3170 g_object_unref (mime_parts);
3174 n_attachments = tny_list_get_length (mime_parts);
3175 if (n_attachments == 1) {
3179 iter = tny_list_create_iterator (mime_parts);
3180 part = (TnyMimePart *) tny_iterator_get_current (iter);
3181 g_object_unref (iter);
3182 if (modest_tny_mime_part_is_msg (part)) {
3184 header = tny_msg_get_header (TNY_MSG (part));
3185 filename = tny_header_dup_subject (header);
3186 g_object_unref (header);
3187 if (filename == NULL)
3188 filename = g_strdup (_("mail_va_no_subject"));
3190 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3192 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3194 g_object_unref (part);
3196 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3197 "mcen_nc_purge_files_text",
3198 n_attachments), n_attachments);
3200 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3201 confirmation_message);
3202 g_free (confirmation_message);
3204 if (response != GTK_RESPONSE_OK) {
3205 g_object_unref (mime_parts);
3209 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3211 iter = tny_list_create_iterator (mime_parts);
3212 while (!tny_iterator_is_done (iter)) {
3215 part = (TnyMimePart *) tny_iterator_get_current (iter);
3216 tny_mime_part_set_purged (TNY_MIME_PART (part));
3217 g_object_unref (part);
3218 tny_iterator_next (iter);
3220 g_object_unref (iter);
3222 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3223 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3224 tny_msg_rewrite_cache (msg);
3225 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3226 g_object_unref (msg);
3227 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3229 g_object_unref (mime_parts);
3231 if (priv->purge_timeout > 0) {
3232 g_source_remove (priv->purge_timeout);
3233 priv->purge_timeout = 0;
3236 if (priv->remove_attachment_banner) {
3237 gtk_widget_destroy (priv->remove_attachment_banner);
3238 g_object_unref (priv->remove_attachment_banner);
3239 priv->remove_attachment_banner = NULL;
3245 update_window_title (ModestMsgViewWindow *window)
3247 ModestMsgViewWindowPrivate *priv;
3249 TnyHeader *header = NULL;
3250 gchar *subject = NULL;
3252 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3254 /* Note that if the window is closed while we're retrieving
3255 the message, this widget could de deleted */
3256 if (!priv->msg_view)
3259 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3261 if (priv->other_body) {
3264 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3266 g_strstrip (description);
3267 subject = description;
3269 } else if (msg != NULL) {
3270 header = tny_msg_get_header (msg);
3271 subject = tny_header_dup_subject (header);
3272 g_object_unref (header);
3273 g_object_unref (msg);
3276 if ((subject == NULL)||(subject[0] == '\0')) {
3278 subject = g_strdup (_("mail_va_no_subject"));
3281 gtk_window_set_title (GTK_WINDOW (window), subject);
3286 on_move_focus (GtkWidget *widget,
3287 GtkDirectionType direction,
3290 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3294 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3296 GnomeVFSResult result;
3297 GnomeVFSHandle *handle = NULL;
3298 GnomeVFSFileInfo *info = NULL;
3301 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3302 if (result != GNOME_VFS_OK) {
3307 info = gnome_vfs_file_info_new ();
3308 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3309 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3310 /* We put a "safe" default size for going to cache */
3311 *expected_size = (300*1024);
3313 *expected_size = info->size;
3315 gnome_vfs_file_info_unref (info);
3317 stream = tny_vfs_stream_new (handle);
3326 TnyStream *output_stream;
3327 GtkWidget *msg_view;
3332 on_fetch_image_idle_refresh_view (gpointer userdata)
3335 FetchImageData *fidata = (FetchImageData *) userdata;
3337 gdk_threads_enter ();
3338 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3339 ModestMsgViewWindowPrivate *priv;
3341 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3342 priv->fetching_images--;
3343 gtk_widget_queue_draw (fidata->msg_view);
3344 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3346 gdk_threads_leave ();
3348 g_object_unref (fidata->msg_view);
3349 g_object_unref (fidata->window);
3350 g_slice_free (FetchImageData, fidata);
3355 on_fetch_image_thread (gpointer userdata)
3357 FetchImageData *fidata = (FetchImageData *) userdata;
3358 TnyStreamCache *cache;
3359 TnyStream *cache_stream;
3361 cache = modest_runtime_get_images_cache ();
3363 tny_stream_cache_get_stream (cache,
3365 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3366 (gpointer) fidata->uri);
3367 g_free (fidata->cache_id);
3368 g_free (fidata->uri);
3370 if (cache_stream != NULL) {
3373 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3376 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3377 if (G_UNLIKELY (nb_read < 0)) {
3379 } else if (G_LIKELY (nb_read > 0)) {
3380 gssize nb_written = 0;
3382 while (G_UNLIKELY (nb_written < nb_read)) {
3385 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3386 nb_read - nb_written);
3387 if (G_UNLIKELY (len < 0))
3393 tny_stream_close (cache_stream);
3394 g_object_unref (cache_stream);
3397 tny_stream_close (fidata->output_stream);
3398 g_object_unref (fidata->output_stream);
3400 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3406 on_fetch_image (ModestMsgView *msgview,
3409 ModestMsgViewWindow *window)
3411 const gchar *current_account;
3412 ModestMsgViewWindowPrivate *priv;
3413 FetchImageData *fidata;
3415 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3417 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3419 fidata = g_slice_new0 (FetchImageData);
3420 fidata->msg_view = g_object_ref (msgview);
3421 fidata->window = g_object_ref (window);
3422 fidata->uri = g_strdup (uri);
3423 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3424 fidata->output_stream = g_object_ref (stream);
3426 priv->fetching_images++;
3427 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3428 g_object_unref (fidata->output_stream);
3429 g_free (fidata->cache_id);
3430 g_free (fidata->uri);
3431 g_object_unref (fidata->msg_view);
3432 g_slice_free (FetchImageData, fidata);
3433 tny_stream_close (stream);
3434 priv->fetching_images--;
3435 update_progress_hint (window);
3438 update_progress_hint (window);
3444 setup_menu (ModestMsgViewWindow *self)
3446 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3448 /* Settings menu buttons */
3449 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3450 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3451 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3453 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3454 dngettext(GETTEXT_PACKAGE,
3455 "mcen_me_move_message",
3456 "mcen_me_move_messages",
3459 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3460 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3462 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3463 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3464 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3466 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3467 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3468 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3470 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3471 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3472 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3474 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3475 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3476 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3477 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3478 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3479 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3481 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3482 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3483 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3484 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3485 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3486 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3488 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3489 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3490 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3494 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3496 ModestMsgViewWindowPrivate *priv;
3497 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3498 GSList *recipients = NULL;
3500 gboolean contacts_to_add = FALSE;
3502 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3506 header = modest_msg_view_window_get_header (self);
3509 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3510 g_object_unref (header);
3512 recipients = modest_tny_msg_get_all_recipients_list (msg);
3513 g_object_unref (msg);
3516 if (recipients != NULL) {
3517 GtkWidget *picker_dialog;
3518 GtkWidget *selector;
3520 gchar *selected = NULL;
3522 selector = hildon_touch_selector_new_text ();
3523 g_object_ref (selector);
3525 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3526 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3527 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3528 (const gchar *) node->data);
3529 contacts_to_add = TRUE;
3533 if (contacts_to_add) {
3536 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3537 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3539 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3540 HILDON_TOUCH_SELECTOR (selector));
3542 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3544 if (picker_result == GTK_RESPONSE_OK) {
3545 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3547 gtk_widget_destroy (picker_dialog);
3550 modest_address_book_add_address (selected, (GtkWindow *) self);
3555 g_object_unref (selector);
3560 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3564 _modest_msg_view_window_map_event (GtkWidget *widget,
3568 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3570 update_progress_hint (self);
3576 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3578 ModestMsgViewWindowPrivate *priv;
3579 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3581 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3585 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3587 ModestMsgViewWindowPrivate *priv;
3588 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3590 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3592 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3596 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3598 ModestMsgViewWindowPrivate *priv;
3601 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3603 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3604 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3606 if (!message_reader (self, priv, header, NULL, NULL, priv->row_reference)) {
3607 g_warning ("Shouldn't happen, trying to reload a message failed");
3610 g_object_unref (header);
3614 update_branding (ModestMsgViewWindow *self)
3616 const gchar *account;
3617 const gchar *mailbox;
3618 ModestAccountMgr *mgr;
3619 ModestProtocol *protocol = NULL;
3620 gchar *service_name = NULL;
3621 const GdkPixbuf *service_icon = NULL;
3622 ModestMsgViewWindowPrivate *priv;
3624 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3626 account = modest_window_get_active_account (MODEST_WINDOW (self));
3627 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3629 mgr = modest_runtime_get_account_mgr ();
3631 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3632 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3633 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3635 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3636 account, mailbox, MODEST_ICON_SIZE_SMALL);
3640 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3641 g_free (service_name);