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), "fetch_image",
762 G_CALLBACK (on_fetch_image), obj);
764 g_signal_connect (G_OBJECT (obj), "key-release-event",
765 G_CALLBACK (modest_msg_view_window_key_event),
768 g_signal_connect (G_OBJECT (obj), "key-press-event",
769 G_CALLBACK (modest_msg_view_window_key_event),
772 g_signal_connect (G_OBJECT (obj), "move-focus",
773 G_CALLBACK (on_move_focus), obj);
775 g_signal_connect (G_OBJECT (obj), "map-event",
776 G_CALLBACK (_modest_msg_view_window_map_event),
779 /* Mail Operation Queue */
780 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
782 G_CALLBACK (on_queue_changed),
785 /* Account manager */
786 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
788 G_CALLBACK(on_account_removed),
791 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
792 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
794 /* First add out toolbar ... */
795 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
797 /* ... and later the find toolbar. This way find toolbar will
798 be shown over the other */
799 priv->find_toolbar = hildon_find_toolbar_new (NULL);
800 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
801 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
802 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
803 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
804 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
805 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
806 priv->last_search = NULL;
808 /* Init the clipboard actions dim status */
809 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
811 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
816 /* FIXME: parameter checks */
818 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
819 const gchar *modest_account_name,
820 const gchar *mailbox,
821 const gchar *msg_uid,
823 GtkTreeRowReference *row_reference)
825 ModestMsgViewWindow *window = NULL;
826 ModestMsgViewWindowPrivate *priv = NULL;
827 TnyFolder *header_folder = NULL;
828 ModestHeaderView *header_view = NULL;
829 ModestWindowMgr *mgr = NULL;
832 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
835 mgr = modest_runtime_get_window_mgr ();
836 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
837 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
839 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
841 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
843 /* Remember the message list's TreeModel so we can detect changes
844 * and change the list selection when necessary: */
845 header_folder = modest_header_view_get_folder (header_view);
847 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
848 TNY_FOLDER_TYPE_OUTBOX);
849 priv->header_folder_id = tny_folder_get_id (header_folder);
850 g_object_unref(header_folder);
853 /* Setup row references and connect signals */
854 priv->header_model = g_object_ref (model);
857 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
858 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
859 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
861 priv->row_reference = NULL;
862 priv->next_row_reference = NULL;
865 /* Connect signals */
866 priv->row_changed_handler =
867 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
868 G_CALLBACK(modest_msg_view_window_on_row_changed),
870 priv->row_deleted_handler =
871 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
872 G_CALLBACK(modest_msg_view_window_on_row_deleted),
874 priv->row_inserted_handler =
875 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
876 G_CALLBACK(modest_msg_view_window_on_row_inserted),
878 priv->rows_reordered_handler =
879 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
880 G_CALLBACK(modest_msg_view_window_on_row_reordered),
883 if (header_view != NULL){
884 modest_header_view_add_observer(header_view,
885 MODEST_HEADER_VIEW_OBSERVER(window));
888 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
889 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
890 update_branding (MODEST_MSG_VIEW_WINDOW (window));
892 /* gtk_widget_show_all (GTK_WIDGET (window)); */
893 modest_msg_view_window_update_priority (window);
894 /* Check dimming rules */
895 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
896 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
897 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
899 return MODEST_WINDOW(window);
903 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
904 const gchar *mailbox,
905 const gchar *msg_uid)
907 ModestMsgViewWindow *window = NULL;
908 ModestMsgViewWindowPrivate *priv = NULL;
909 ModestWindowMgr *mgr = NULL;
911 TnyAccount *account = NULL;
913 mgr = modest_runtime_get_window_mgr ();
914 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
915 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
917 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
919 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
923 is_merge = g_str_has_prefix (msg_uid, "merge:");
925 /* Get the account */
927 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
931 if (is_merge || account) {
932 TnyFolder *folder = NULL;
934 /* Try to get the message, if it's already downloaded
935 we don't need to connect */
937 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
939 ModestTnyAccountStore *account_store;
940 ModestTnyLocalFoldersAccount *local_folders_account;
942 account_store = modest_runtime_get_account_store ();
943 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
944 modest_tny_account_store_get_local_folders_account (account_store));
945 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
946 g_object_unref (local_folders_account);
950 gboolean device_online;
952 device = modest_runtime_get_device();
953 device_online = tny_device_is_online (device);
955 message_reader (window, priv, NULL, msg_uid, folder, NULL);
957 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
959 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
960 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
961 update_branding (MODEST_MSG_VIEW_WINDOW (window));
962 g_object_unref (msg);
964 message_reader (window, priv, NULL, msg_uid, folder, NULL);
967 g_object_unref (folder);
972 /* Check dimming rules */
973 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
974 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
975 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
977 return MODEST_WINDOW(window);
981 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
982 const gchar *modest_account_name,
983 const gchar *mailbox,
984 const gchar *msg_uid,
985 GtkTreeRowReference *row_reference)
987 ModestMsgViewWindow *window = NULL;
988 ModestMsgViewWindowPrivate *priv = NULL;
989 TnyFolder *header_folder = NULL;
990 ModestWindowMgr *mgr = NULL;
994 mgr = modest_runtime_get_window_mgr ();
995 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
996 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
998 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1000 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1002 /* Remember the message list's TreeModel so we can detect changes
1003 * and change the list selection when necessary: */
1005 if (header_view != NULL){
1006 header_folder = modest_header_view_get_folder(header_view);
1007 /* This could happen if the header folder was
1008 unseleted before opening this msg window (for
1009 example if the user selects an account in the
1010 folder view of the main window */
1011 if (header_folder) {
1012 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1013 TNY_FOLDER_TYPE_OUTBOX);
1014 priv->header_folder_id = tny_folder_get_id(header_folder);
1015 g_object_unref(header_folder);
1019 /* Setup row references and connect signals */
1020 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1021 g_object_ref (priv->header_model);
1023 if (row_reference) {
1024 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1025 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1026 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1028 priv->row_reference = NULL;
1029 priv->next_row_reference = NULL;
1032 /* Connect signals */
1033 priv->row_changed_handler =
1034 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1035 G_CALLBACK(modest_msg_view_window_on_row_changed),
1037 priv->row_deleted_handler =
1038 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1039 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1041 priv->row_inserted_handler =
1042 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1043 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1045 priv->rows_reordered_handler =
1046 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1047 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1050 if (header_view != NULL){
1051 modest_header_view_add_observer(header_view,
1052 MODEST_HEADER_VIEW_OBSERVER(window));
1055 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1056 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1058 path = gtk_tree_row_reference_get_path (row_reference);
1059 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1061 gtk_tree_model_get (priv->header_model, &iter,
1062 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1064 message_reader (window, priv, header, NULL, NULL, row_reference);
1065 g_object_unref (header);
1067 gtk_tree_path_free (path);
1069 /* Check dimming rules */
1070 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1071 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1072 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1074 return MODEST_WINDOW(window);
1078 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1079 const gchar *modest_account_name,
1080 const gchar *mailbox,
1081 const gchar *msg_uid)
1083 ModestMsgViewWindow *window = NULL;
1084 ModestMsgViewWindowPrivate *priv = NULL;
1085 ModestWindowMgr *mgr = NULL;
1087 mgr = modest_runtime_get_window_mgr ();
1088 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1089 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1090 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1092 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1094 /* Remember that this is a search result,
1095 * so we can disable some UI appropriately: */
1096 priv->is_search_result = TRUE;
1098 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1099 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1101 update_window_title (window);
1102 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1103 modest_msg_view_window_update_priority (window);
1105 /* Check dimming rules */
1106 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1107 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1108 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1110 return MODEST_WINDOW(window);
1114 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1116 ModestMsgViewWindowPrivate *priv = NULL;
1118 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1119 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1121 return (priv->other_body != NULL);
1125 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1126 TnyMimePart *other_body,
1127 const gchar *modest_account_name,
1128 const gchar *mailbox,
1129 const gchar *msg_uid)
1131 GObject *obj = NULL;
1132 ModestMsgViewWindowPrivate *priv;
1133 ModestWindowMgr *mgr = NULL;
1135 g_return_val_if_fail (msg, NULL);
1136 mgr = modest_runtime_get_window_mgr ();
1137 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1138 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1139 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1140 modest_account_name, mailbox, msg_uid);
1143 priv->other_body = g_object_ref (other_body);
1144 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1146 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1148 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1149 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1151 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1153 /* Check dimming rules */
1154 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1155 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1156 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1158 return MODEST_WINDOW(obj);
1162 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1163 const gchar *modest_account_name,
1164 const gchar *mailbox,
1165 const gchar *msg_uid)
1167 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1171 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1174 ModestMsgViewWindow *window)
1176 check_dimming_rules_after_change (window);
1180 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1182 ModestMsgViewWindow *window)
1184 check_dimming_rules_after_change (window);
1186 /* The window could have dissapeared */
1189 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1191 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1192 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1196 /* On insertions we check if the folder still has the message we are
1197 * showing or do not. If do not, we do nothing. Which means we are still
1198 * not attached to any header folder and thus next/prev buttons are
1199 * still dimmed. Once the message that is shown by msg-view is found, the
1200 * new model of header-view will be attached and the references will be set.
1201 * On each further insertions dimming rules will be checked. However
1202 * this requires extra CPU time at least works.
1203 * (An message might be deleted from TnyFolder and thus will not be
1204 * inserted into the model again for example if it is removed by the
1205 * imap server and the header view is refreshed.)
1208 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1209 GtkTreePath *tree_path,
1210 GtkTreeIter *tree_iter,
1211 ModestMsgViewWindow *window)
1213 ModestMsgViewWindowPrivate *priv = NULL;
1214 TnyHeader *header = NULL;
1216 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1217 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1219 g_assert (model == priv->header_model);
1221 /* Check if the newly inserted message is the same we are actually
1222 * showing. IF not, we should remain detached from the header model
1223 * and thus prev and next toolbar buttons should remain dimmed. */
1224 gtk_tree_model_get (model, tree_iter,
1225 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1228 if (TNY_IS_HEADER (header)) {
1231 uid = modest_tny_folder_get_header_unique_id (header);
1232 if (!g_str_equal(priv->msg_uid, uid)) {
1233 check_dimming_rules_after_change (window);
1235 g_object_unref (G_OBJECT(header));
1239 g_object_unref(G_OBJECT(header));
1242 if (priv->row_reference) {
1243 gtk_tree_row_reference_free (priv->row_reference);
1246 /* Setup row_reference for the actual msg. */
1247 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1248 if (priv->row_reference == NULL) {
1249 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1253 /* Now set up next_row_reference. */
1254 if (priv->next_row_reference) {
1255 gtk_tree_row_reference_free (priv->next_row_reference);
1258 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1259 select_next_valid_row (priv->header_model,
1260 &(priv->next_row_reference), FALSE, priv->is_outbox);
1262 /* Connect the remaining callbacks to become able to detect
1263 * changes in header-view. */
1264 priv->row_changed_handler =
1265 g_signal_connect (priv->header_model, "row-changed",
1266 G_CALLBACK (modest_msg_view_window_on_row_changed),
1268 priv->row_deleted_handler =
1269 g_signal_connect (priv->header_model, "row-deleted",
1270 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1272 priv->rows_reordered_handler =
1273 g_signal_connect (priv->header_model, "rows-reordered",
1274 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1277 check_dimming_rules_after_change (window);
1281 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1285 ModestMsgViewWindow *window)
1287 ModestMsgViewWindowPrivate *priv = NULL;
1288 gboolean already_changed = FALSE;
1290 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1292 /* If the current row was reordered select the proper next
1293 valid row. The same if the next row reference changes */
1294 if (!priv->row_reference ||
1295 !gtk_tree_row_reference_valid (priv->row_reference))
1298 if (priv->next_row_reference &&
1299 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1300 GtkTreePath *cur, *next;
1301 /* Check that the order is still the correct one */
1302 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1303 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1304 gtk_tree_path_next (cur);
1305 if (gtk_tree_path_compare (cur, next) != 0) {
1306 gtk_tree_row_reference_free (priv->next_row_reference);
1307 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1308 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1309 already_changed = TRUE;
1311 gtk_tree_path_free (cur);
1312 gtk_tree_path_free (next);
1314 if (priv->next_row_reference)
1315 gtk_tree_row_reference_free (priv->next_row_reference);
1316 /* Update next row reference */
1317 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1318 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1319 already_changed = TRUE;
1322 check_dimming_rules_after_change (window);
1325 /* The modest_msg_view_window_update_model_replaced implements update
1326 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1327 * actually belongs to the header-view is the same as the TnyFolder of
1328 * the message of msg-view or not. If they are different, there is
1329 * nothing to do. If they are the same, then the model has replaced and
1330 * the reference in msg-view shall be replaced from the old model to
1331 * the new model. In this case the view will be detached from it's
1332 * header folder. From this point the next/prev buttons are dimmed.
1335 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1336 GtkTreeModel *model,
1337 const gchar *tny_folder_id)
1339 ModestMsgViewWindowPrivate *priv = NULL;
1340 ModestMsgViewWindow *window = NULL;
1342 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1343 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1345 window = MODEST_MSG_VIEW_WINDOW(observer);
1346 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1348 /* If there is an other folder in the header-view then we do
1349 * not care about it's model (msg list). Else if the
1350 * header-view shows the folder the msg shown by us is in, we
1351 * shall replace our model reference and make some check. */
1352 if(model == NULL || tny_folder_id == NULL ||
1353 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1356 /* Model is changed(replaced), so we should forget the old
1357 * one. Because there might be other references and there
1358 * might be some change on the model even if we unreferenced
1359 * it, we need to disconnect our signals here. */
1360 if (priv->header_model) {
1361 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1362 priv->row_changed_handler))
1363 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1364 priv->row_changed_handler);
1365 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1366 priv->row_deleted_handler))
1367 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1368 priv->row_deleted_handler);
1369 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1370 priv->row_inserted_handler))
1371 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1372 priv->row_inserted_handler);
1373 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1374 priv->rows_reordered_handler))
1375 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1376 priv->rows_reordered_handler);
1379 if (priv->row_reference)
1380 gtk_tree_row_reference_free (priv->row_reference);
1381 if (priv->next_row_reference)
1382 gtk_tree_row_reference_free (priv->next_row_reference);
1383 g_object_unref(priv->header_model);
1386 priv->row_changed_handler = 0;
1387 priv->row_deleted_handler = 0;
1388 priv->row_inserted_handler = 0;
1389 priv->rows_reordered_handler = 0;
1390 priv->next_row_reference = NULL;
1391 priv->row_reference = NULL;
1392 priv->header_model = NULL;
1395 priv->header_model = g_object_ref (model);
1397 /* Also we must connect to the new model for row insertions.
1398 * Only for insertions now. We will need other ones only after
1399 * the msg is show by msg-view is added to the new model. */
1400 priv->row_inserted_handler =
1401 g_signal_connect (priv->header_model, "row-inserted",
1402 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1405 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1406 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1410 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1412 ModestMsgViewWindowPrivate *priv= NULL;
1414 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1415 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1417 return priv->progress_hint;
1421 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1423 ModestMsgViewWindowPrivate *priv= NULL;
1425 TnyHeader *header = NULL;
1426 GtkTreePath *path = NULL;
1429 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1430 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1432 /* If the message was not obtained from a treemodel,
1433 * for instance if it was opened directly by the search UI:
1435 if (priv->header_model == NULL ||
1436 priv->row_reference == NULL ||
1437 !gtk_tree_row_reference_valid (priv->row_reference)) {
1438 msg = modest_msg_view_window_get_message (self);
1440 header = tny_msg_get_header (msg);
1441 g_object_unref (msg);
1446 /* Get iter of the currently selected message in the header view: */
1447 path = gtk_tree_row_reference_get_path (priv->row_reference);
1448 g_return_val_if_fail (path != NULL, NULL);
1449 gtk_tree_model_get_iter (priv->header_model,
1453 /* Get current message header */
1454 gtk_tree_model_get (priv->header_model, &iter,
1455 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1458 gtk_tree_path_free (path);
1463 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1465 ModestMsgViewWindowPrivate *priv;
1467 g_return_val_if_fail (self, NULL);
1469 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1471 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1475 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1477 ModestMsgViewWindowPrivate *priv;
1479 g_return_val_if_fail (self, NULL);
1481 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1483 return (const gchar*) priv->msg_uid;
1486 /* Used for the Ctrl+F accelerator */
1488 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1491 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1492 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1494 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1495 modest_msg_view_window_find_toolbar_close (obj, data);
1497 modest_msg_view_window_show_find_toolbar (obj, data);
1501 /* Handler for menu option */
1503 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1506 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1507 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1509 gtk_widget_show (priv->find_toolbar);
1510 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1513 /* Handler for click on the "X" close button in find toolbar */
1515 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1516 ModestMsgViewWindow *obj)
1518 ModestMsgViewWindowPrivate *priv;
1520 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1523 gtk_widget_hide (priv->find_toolbar);
1524 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1528 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1529 ModestMsgViewWindow *obj)
1531 gchar *current_search;
1532 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1534 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1535 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1539 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1541 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1542 g_free (current_search);
1543 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1547 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1549 g_free (priv->last_search);
1550 priv->last_search = g_strdup (current_search);
1551 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1554 hildon_banner_show_information (NULL, NULL,
1555 _HL("ckct_ib_find_no_matches"));
1556 g_free (priv->last_search);
1557 priv->last_search = NULL;
1559 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1562 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1563 hildon_banner_show_information (NULL, NULL,
1564 _HL("ckct_ib_find_search_complete"));
1565 g_free (priv->last_search);
1566 priv->last_search = NULL;
1568 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1572 g_free (current_search);
1577 modest_msg_view_window_set_zoom (ModestWindow *window,
1580 ModestMsgViewWindowPrivate *priv;
1581 ModestWindowPrivate *parent_priv;
1583 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1585 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1586 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1587 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1592 modest_msg_view_window_get_zoom (ModestWindow *window)
1594 ModestMsgViewWindowPrivate *priv;
1596 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1598 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1599 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1603 modest_msg_view_window_zoom_plus (ModestWindow *window)
1606 ModestMsgViewWindowPrivate *priv;
1610 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1611 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1613 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1615 if (zoom_level >= 2.0) {
1616 hildon_banner_show_information (NULL, NULL,
1617 _CS("ckct_ib_max_zoom_level_reached"));
1619 } else if (zoom_level >= 1.5) {
1621 } else if (zoom_level >= 1.2) {
1623 } else if (zoom_level >= 1.0) {
1625 } else if (zoom_level >= 0.8) {
1627 } else if (zoom_level >= 0.5) {
1633 /* set zoom level */
1634 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1635 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1636 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1637 g_free (banner_text);
1638 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1644 modest_msg_view_window_zoom_minus (ModestWindow *window)
1647 ModestMsgViewWindowPrivate *priv;
1651 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1652 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1654 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1656 if (zoom_level <= 0.5) {
1657 hildon_banner_show_information (NULL, NULL,
1658 _CS("ckct_ib_min_zoom_level_reached"));
1660 } else if (zoom_level <= 0.8) {
1662 } else if (zoom_level <= 1.0) {
1664 } else if (zoom_level <= 1.2) {
1666 } else if (zoom_level <= 1.5) {
1668 } else if (zoom_level <= 2.0) {
1674 /* set zoom level */
1675 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1676 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1677 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1678 g_free (banner_text);
1679 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1686 modest_msg_view_window_key_event (GtkWidget *window,
1692 focus = gtk_window_get_focus (GTK_WINDOW (window));
1694 /* for the find toolbar case */
1695 if (focus && GTK_IS_ENTRY (focus)) {
1696 if (event->keyval == GDK_BackSpace) {
1698 copy = gdk_event_copy ((GdkEvent *) event);
1699 gtk_widget_event (focus, copy);
1700 gdk_event_free (copy);
1705 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1706 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1707 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1708 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1709 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1710 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1711 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1712 /* gboolean return_value; */
1714 if (event->type == GDK_KEY_PRESS) {
1715 GtkScrollType scroll_type;
1717 switch (event->keyval) {
1720 scroll_type = GTK_SCROLL_STEP_UP; break;
1723 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1725 case GDK_KP_Page_Up:
1726 scroll_type = GTK_SCROLL_PAGE_UP; break;
1728 case GDK_KP_Page_Down:
1729 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1732 scroll_type = GTK_SCROLL_START; break;
1735 scroll_type = GTK_SCROLL_END; break;
1736 default: scroll_type = GTK_SCROLL_NONE;
1739 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1740 /* scroll_type, FALSE, &return_value); */
1751 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1754 ModestMsgViewWindowPrivate *priv;
1755 GtkTreeIter tmp_iter;
1756 gboolean is_last_selected;
1758 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1759 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1761 /*if no model (so no rows at all), then virtually we are the last*/
1762 if (!priv->header_model || !priv->row_reference)
1765 if (!gtk_tree_row_reference_valid (priv->row_reference))
1768 path = gtk_tree_row_reference_get_path (priv->row_reference);
1772 is_last_selected = TRUE;
1773 while (is_last_selected) {
1775 gtk_tree_path_next (path);
1776 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1778 gtk_tree_model_get (priv->header_model, &tmp_iter,
1779 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1782 if (msg_is_visible (header, priv->is_outbox))
1783 is_last_selected = FALSE;
1784 g_object_unref(G_OBJECT(header));
1787 gtk_tree_path_free (path);
1788 return is_last_selected;
1792 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1794 ModestMsgViewWindowPrivate *priv;
1796 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1797 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1799 return priv->header_model != NULL;
1803 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1805 ModestMsgViewWindowPrivate *priv;
1807 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1808 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1810 return priv->is_search_result;
1814 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1816 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1818 if (!check_outbox) {
1821 ModestTnySendQueueStatus status;
1822 status = modest_tny_all_send_queues_get_msg_status (header);
1823 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1824 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1829 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1832 ModestMsgViewWindowPrivate *priv;
1833 gboolean is_first_selected;
1834 GtkTreeIter tmp_iter;
1836 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1837 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1839 /*if no model (so no rows at all), then virtually we are the first*/
1840 if (!priv->header_model || !priv->row_reference)
1843 if (!gtk_tree_row_reference_valid (priv->row_reference))
1846 path = gtk_tree_row_reference_get_path (priv->row_reference);
1850 is_first_selected = TRUE;
1851 while (is_first_selected) {
1853 if(!gtk_tree_path_prev (path))
1855 /* Here the 'if' is needless for logic, but let make sure
1856 * iter is valid for gtk_tree_model_get. */
1857 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1859 gtk_tree_model_get (priv->header_model, &tmp_iter,
1860 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1863 if (msg_is_visible (header, priv->is_outbox))
1864 is_first_selected = FALSE;
1865 g_object_unref(G_OBJECT(header));
1868 gtk_tree_path_free (path);
1869 return is_first_selected;
1876 GtkTreeRowReference *row_reference;
1880 message_reader_performer (gboolean canceled,
1882 GtkWindow *parent_window,
1883 TnyAccount *account,
1886 ModestMailOperation *mail_op = NULL;
1887 MsgReaderInfo *info;
1889 info = (MsgReaderInfo *) user_data;
1890 if (canceled || err) {
1891 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1895 /* Register the header - it'll be unregistered in the callback */
1897 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1899 /* New mail operation */
1900 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1901 modest_ui_actions_disk_operations_error_handler,
1904 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1906 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1908 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1909 g_object_unref (mail_op);
1911 /* Update dimming rules */
1912 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1913 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1916 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1917 g_free (info->msg_uid);
1919 g_object_unref (info->folder);
1921 g_object_unref (info->header);
1922 g_slice_free (MsgReaderInfo, info);
1927 * Reads the message whose summary item is @header. It takes care of
1928 * several things, among others:
1930 * If the message was not previously downloaded then ask the user
1931 * before downloading. If there is no connection launch the connection
1932 * dialog. Update toolbar dimming rules.
1934 * Returns: TRUE if the mail operation was started, otherwise if the
1935 * user do not want to download the message, or if the user do not
1936 * want to connect, then the operation is not issued
1939 message_reader (ModestMsgViewWindow *window,
1940 ModestMsgViewWindowPrivate *priv,
1942 const gchar *msg_uid,
1944 GtkTreeRowReference *row_reference)
1946 ModestWindowMgr *mgr;
1947 TnyAccount *account;
1948 MsgReaderInfo *info;
1950 /* We set the header from model while we're loading */
1951 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1952 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1955 g_object_ref (folder);
1957 mgr = modest_runtime_get_window_mgr ();
1958 /* Msg download completed */
1959 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1961 /* Ask the user if he wants to download the message if
1963 if (!tny_device_is_online (modest_runtime_get_device())) {
1964 GtkResponseType response;
1966 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1967 _("mcen_nc_get_msg"));
1968 if (response == GTK_RESPONSE_CANCEL) {
1969 update_window_title (window);
1974 folder = tny_header_get_folder (header);
1976 info = g_slice_new (MsgReaderInfo);
1977 info->msg_uid = g_strdup (msg_uid);
1979 info->header = g_object_ref (header);
1981 info->header = NULL;
1983 info->folder = g_object_ref (folder);
1985 info->folder = NULL;
1986 if (row_reference) {
1987 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1989 info->row_reference = NULL;
1992 /* Offer the connection dialog if necessary */
1993 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1995 TNY_FOLDER_STORE (folder),
1996 message_reader_performer,
1998 g_object_unref (folder);
2004 folder = tny_header_get_folder (header);
2006 account = tny_folder_get_account (folder);
2007 info = g_slice_new (MsgReaderInfo);
2008 info->msg_uid = g_strdup (msg_uid);
2010 info->folder = g_object_ref (folder);
2012 info->folder = NULL;
2014 info->header = g_object_ref (header);
2016 info->header = NULL;
2018 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2020 info->row_reference = NULL;
2022 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2023 g_object_unref (account);
2024 g_object_unref (folder);
2030 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2032 ModestMsgViewWindowPrivate *priv;
2033 GtkTreePath *path= NULL;
2034 GtkTreeIter tmp_iter;
2036 gboolean retval = TRUE;
2037 GtkTreeRowReference *row_reference = NULL;
2039 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2040 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2042 if (!priv->row_reference)
2045 /* Update the next row reference if it's not valid. This could
2046 happen if for example the header which it was pointing to,
2047 was deleted. The best place to do it is in the row-deleted
2048 handler but the tinymail model do not work like the glib
2049 tree models and reports the deletion when the row is still
2051 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2052 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2053 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2054 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2057 if (priv->next_row_reference)
2058 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2062 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2064 gtk_tree_model_get_iter (priv->header_model,
2067 gtk_tree_path_free (path);
2069 gtk_tree_model_get (priv->header_model, &tmp_iter,
2070 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2073 /* Read the message & show it */
2074 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2077 gtk_tree_row_reference_free (row_reference);
2080 g_object_unref (header);
2086 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2088 ModestMsgViewWindowPrivate *priv = NULL;
2090 gboolean finished = FALSE;
2091 gboolean retval = FALSE;
2093 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2094 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2096 /* Return inmediatly if there is no header model */
2097 if (!priv->header_model || !priv->row_reference)
2100 path = gtk_tree_row_reference_get_path (priv->row_reference);
2101 while (!finished && gtk_tree_path_prev (path)) {
2105 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2106 gtk_tree_model_get (priv->header_model, &iter,
2107 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2111 if (msg_is_visible (header, priv->is_outbox)) {
2112 GtkTreeRowReference *row_reference;
2113 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2114 /* Read the message & show it */
2115 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2116 gtk_tree_row_reference_free (row_reference);
2120 g_object_unref (header);
2124 gtk_tree_path_free (path);
2129 view_msg_cb (ModestMailOperation *mail_op,
2136 ModestMsgViewWindow *self = NULL;
2137 ModestMsgViewWindowPrivate *priv = NULL;
2138 GtkTreeRowReference *row_reference = NULL;
2140 /* Unregister the header (it was registered before creating the mail operation) */
2141 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2143 row_reference = (GtkTreeRowReference *) user_data;
2146 gtk_tree_row_reference_free (row_reference);
2147 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2149 /* Restore window title */
2150 update_window_title (self);
2151 g_object_unref (self);
2156 /* If there was any error */
2157 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2159 gtk_tree_row_reference_free (row_reference);
2160 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2162 /* Restore window title */
2163 update_window_title (self);
2164 g_object_unref (self);
2169 /* Get the window */
2170 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2171 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2172 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2174 /* Update the row reference */
2175 if (priv->row_reference != NULL) {
2176 gtk_tree_row_reference_free (priv->row_reference);
2177 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2178 if (priv->next_row_reference != NULL) {
2179 gtk_tree_row_reference_free (priv->next_row_reference);
2181 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2182 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2185 /* Mark header as read */
2186 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2187 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2189 /* Set new message */
2190 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2191 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2192 modest_msg_view_window_update_priority (self);
2193 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2194 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2195 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2198 /* Set the new message uid of the window */
2199 if (priv->msg_uid) {
2200 g_free (priv->msg_uid);
2201 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2204 /* Notify the observers */
2205 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2206 0, priv->header_model, priv->row_reference);
2209 g_object_unref (self);
2211 gtk_tree_row_reference_free (row_reference);
2215 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2217 ModestMsgViewWindowPrivate *priv;
2219 TnyFolderType folder_type;
2221 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2223 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2225 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2229 folder = tny_msg_get_folder (msg);
2231 folder_type = modest_tny_folder_guess_folder_type (folder);
2232 g_object_unref (folder);
2234 g_object_unref (msg);
2242 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2244 ModestMsgViewWindowPrivate *priv;
2245 TnyHeader *header = NULL;
2246 TnyHeaderFlags flags = 0;
2248 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2250 if (priv->header_model && priv->row_reference) {
2252 GtkTreePath *path = NULL;
2254 path = gtk_tree_row_reference_get_path (priv->row_reference);
2255 g_return_if_fail (path != NULL);
2256 gtk_tree_model_get_iter (priv->header_model,
2258 gtk_tree_row_reference_get_path (priv->row_reference));
2260 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2262 gtk_tree_path_free (path);
2265 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2267 header = tny_msg_get_header (msg);
2268 g_object_unref (msg);
2273 flags = tny_header_get_flags (header);
2274 g_object_unref(G_OBJECT(header));
2277 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2282 toolbar_resize (ModestMsgViewWindow *self)
2284 ModestMsgViewWindowPrivate *priv = NULL;
2285 ModestWindowPrivate *parent_priv = NULL;
2287 gint static_button_size;
2288 ModestWindowMgr *mgr;
2290 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2291 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2292 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2294 mgr = modest_runtime_get_window_mgr ();
2295 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2297 if (parent_priv->toolbar) {
2298 /* left size buttons */
2299 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2300 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2301 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2302 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2303 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2304 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2305 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2306 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2307 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2308 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2309 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2310 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2311 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2312 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2313 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2314 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2316 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2317 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2318 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2319 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2324 modest_msg_view_window_show_toolbar (ModestWindow *self,
2325 gboolean show_toolbar)
2327 ModestMsgViewWindowPrivate *priv = NULL;
2328 ModestWindowPrivate *parent_priv;
2330 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2331 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2333 /* Set optimized view status */
2334 priv->optimized_view = !show_toolbar;
2336 if (!parent_priv->toolbar) {
2337 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2339 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2340 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2342 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2343 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2344 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2347 hildon_window_add_toolbar (HILDON_WINDOW (self),
2348 GTK_TOOLBAR (parent_priv->toolbar));
2353 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2354 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2355 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2357 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2358 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2359 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2361 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2364 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2365 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2370 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2372 ModestMsgViewWindow *window)
2374 if (!GTK_WIDGET_VISIBLE (window))
2377 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2381 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2383 ModestMsgViewWindowPrivate *priv;
2385 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2386 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2388 return priv->progress_hint;
2392 observers_empty (ModestMsgViewWindow *self)
2395 ModestMsgViewWindowPrivate *priv;
2396 gboolean is_empty = TRUE;
2397 guint pending_ops = 0;
2399 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2400 tmp = priv->progress_widgets;
2402 /* Check all observers */
2403 while (tmp && is_empty) {
2404 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2405 is_empty = pending_ops == 0;
2407 tmp = g_slist_next(tmp);
2414 on_account_removed (TnyAccountStore *account_store,
2415 TnyAccount *account,
2418 /* Do nothing if it's a transport account, because we only
2419 show the messages of a store account */
2420 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2421 const gchar *parent_acc = NULL;
2422 const gchar *our_acc = NULL;
2424 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2425 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2427 /* Close this window if I'm showing a message of the removed account */
2428 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2429 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2434 on_mail_operation_started (ModestMailOperation *mail_op,
2437 ModestMsgViewWindow *self;
2438 ModestMailOperationTypeOperation op_type;
2440 ModestMsgViewWindowPrivate *priv;
2441 GObject *source = NULL;
2443 self = MODEST_MSG_VIEW_WINDOW (user_data);
2444 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2445 op_type = modest_mail_operation_get_type_operation (mail_op);
2446 tmp = priv->progress_widgets;
2447 source = modest_mail_operation_get_source(mail_op);
2448 if (G_OBJECT (self) == source) {
2449 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2450 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2451 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2452 set_progress_hint (self, TRUE);
2454 modest_progress_object_add_operation (
2455 MODEST_PROGRESS_OBJECT (tmp->data),
2457 tmp = g_slist_next (tmp);
2461 g_object_unref (source);
2463 /* Update dimming rules */
2464 check_dimming_rules_after_change (self);
2468 on_mail_operation_finished (ModestMailOperation *mail_op,
2471 ModestMsgViewWindow *self;
2472 ModestMailOperationTypeOperation op_type;
2474 ModestMsgViewWindowPrivate *priv;
2476 self = MODEST_MSG_VIEW_WINDOW (user_data);
2477 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2478 op_type = modest_mail_operation_get_type_operation (mail_op);
2479 tmp = priv->progress_widgets;
2481 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2482 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2483 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2485 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2487 tmp = g_slist_next (tmp);
2490 /* If no more operations are being observed, NORMAL mode is enabled again */
2491 if (observers_empty (self)) {
2492 set_progress_hint (self, FALSE);
2496 /* Update dimming rules. We have to do this right here
2497 and not in view_msg_cb because at that point the
2498 transfer mode is still enabled so the dimming rule
2499 won't let the user delete the message that has been
2500 readed for example */
2501 check_dimming_rules_after_change (self);
2505 on_queue_changed (ModestMailOperationQueue *queue,
2506 ModestMailOperation *mail_op,
2507 ModestMailOperationQueueNotification type,
2508 ModestMsgViewWindow *self)
2510 ModestMsgViewWindowPrivate *priv;
2512 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2514 /* If this operations was created by another window, do nothing */
2515 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2518 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2519 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2521 "operation-started",
2522 G_CALLBACK (on_mail_operation_started),
2524 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2526 "operation-finished",
2527 G_CALLBACK (on_mail_operation_finished),
2529 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2530 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2532 "operation-started");
2533 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2535 "operation-finished");
2540 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2542 ModestMsgViewWindowPrivate *priv;
2543 TnyList *selected_attachments = NULL;
2545 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2546 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2548 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2549 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2551 return selected_attachments;
2555 ModestMsgViewWindow *self;
2557 } DecodeAsyncHelper;
2560 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2566 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2568 /* It could happen that the window was closed */
2569 if (GTK_WIDGET_VISIBLE (helper->self))
2570 set_progress_hint (helper->self, FALSE);
2572 if (cancelled || err) {
2574 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2575 modest_platform_information_banner (NULL, NULL, msg);
2581 /* make the file read-only */
2582 g_chmod(helper->file_path, 0444);
2584 /* Activate the file */
2585 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2589 g_object_unref (helper->self);
2590 g_free (helper->file_path);
2591 g_slice_free (DecodeAsyncHelper, helper);
2595 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2596 TnyMimePart *mime_part)
2598 ModestMsgViewWindowPrivate *priv;
2599 const gchar *msg_uid;
2600 gchar *attachment_uid = NULL;
2601 gint attachment_index = 0;
2602 TnyList *attachments;
2604 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2605 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2606 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2608 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2609 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2610 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2611 g_object_unref (attachments);
2613 if (msg_uid && attachment_index >= 0) {
2614 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2617 if (mime_part == NULL) {
2618 gboolean error = FALSE;
2619 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2620 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2622 } else if (tny_list_get_length (selected_attachments) > 1) {
2623 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2627 iter = tny_list_create_iterator (selected_attachments);
2628 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2629 g_object_unref (iter);
2631 if (selected_attachments)
2632 g_object_unref (selected_attachments);
2637 g_object_ref (mime_part);
2640 if (tny_mime_part_is_purged (mime_part))
2643 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2644 gchar *filepath = NULL;
2645 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2646 gboolean show_error_banner = FALSE;
2647 TnyFsStream *temp_stream = NULL;
2648 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2651 if (temp_stream != NULL) {
2652 DecodeAsyncHelper *helper;
2654 /* Activate progress hint */
2655 set_progress_hint (window, TRUE);
2657 helper = g_slice_new0 (DecodeAsyncHelper);
2658 helper->self = g_object_ref (window);
2659 helper->file_path = g_strdup (filepath);
2661 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2662 on_decode_to_stream_async_handler,
2665 g_object_unref (temp_stream);
2666 /* NOTE: files in the temporary area will be automatically
2667 * cleaned after some time if they are no longer in use */
2670 const gchar *content_type;
2671 /* the file may already exist but it isn't writable,
2672 * let's try to open it anyway */
2673 content_type = tny_mime_part_get_content_type (mime_part);
2674 modest_platform_activate_file (filepath, content_type);
2676 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2677 show_error_banner = TRUE;
2682 if (show_error_banner)
2683 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2684 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2685 ModestWindowMgr *mgr;
2686 ModestWindow *msg_win = NULL;
2687 TnyMsg *current_msg;
2691 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2692 mgr = modest_runtime_get_window_mgr ();
2693 header = tny_msg_get_header (TNY_MSG (current_msg));
2694 found = modest_window_mgr_find_registered_message_uid (mgr,
2699 g_debug ("window for this body is already being created");
2702 /* it's not found, so create a new window for it */
2703 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2704 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2705 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2707 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2709 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2710 account, mailbox, attachment_uid);
2712 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2713 modest_window_get_zoom (MODEST_WINDOW (window)));
2714 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2715 gtk_widget_show_all (GTK_WIDGET (msg_win));
2717 gtk_widget_destroy (GTK_WIDGET (msg_win));
2719 g_object_unref (current_msg);
2721 /* message attachment */
2722 TnyHeader *header = NULL;
2723 ModestWindowMgr *mgr;
2724 ModestWindow *msg_win = NULL;
2727 header = tny_msg_get_header (TNY_MSG (mime_part));
2728 mgr = modest_runtime_get_window_mgr ();
2729 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2732 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2733 * thus, we don't do anything */
2734 g_debug ("window for is already being created");
2736 /* it's not found, so create a new window for it */
2737 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2738 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2739 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2741 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2742 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2743 mailbox, attachment_uid);
2744 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2745 modest_window_get_zoom (MODEST_WINDOW (window)));
2746 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2747 gtk_widget_show_all (GTK_WIDGET (msg_win));
2749 gtk_widget_destroy (GTK_WIDGET (msg_win));
2755 g_free (attachment_uid);
2757 g_object_unref (mime_part);
2769 GnomeVFSResult result;
2772 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2773 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2774 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2775 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2778 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2782 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2783 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2784 g_free (pair->filename);
2785 g_object_unref (pair->part);
2786 g_slice_free (SaveMimePartPair, pair);
2788 g_list_free (info->pairs);
2791 g_slice_free (SaveMimePartInfo, info);
2796 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2798 /* This is a GDK lock because we are an idle callback and
2799 * hildon_banner_show_information is or does Gtk+ code */
2801 gdk_threads_enter (); /* CHECKED */
2802 if (info->result == GNOME_VFS_OK) {
2803 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2804 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2805 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2806 modest_platform_information_banner (NULL, NULL, msg);
2809 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2811 save_mime_part_info_free (info, FALSE);
2812 gdk_threads_leave (); /* CHECKED */
2818 save_mime_part_to_file (SaveMimePartInfo *info)
2820 GnomeVFSHandle *handle;
2822 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2824 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2825 if (info->result == GNOME_VFS_OK) {
2826 GError *error = NULL;
2827 stream = tny_vfs_stream_new (handle);
2828 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2829 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2831 if ((error->domain == TNY_ERROR_DOMAIN) &&
2832 (error->code == TNY_IO_ERROR_WRITE) &&
2833 (errno == ENOSPC)) {
2834 info->result = GNOME_VFS_ERROR_NO_SPACE;
2836 info->result = GNOME_VFS_ERROR_IO;
2839 g_object_unref (G_OBJECT (stream));
2841 g_warning ("Could not create save attachment %s: %s\n",
2842 pair->filename, gnome_vfs_result_to_string (info->result));
2845 /* Go on saving remaining files */
2846 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2847 if (info->pairs != NULL) {
2848 save_mime_part_to_file (info);
2850 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2857 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2858 SaveMimePartInfo *info)
2860 gboolean is_ok = TRUE;
2861 gint replaced_files = 0;
2862 const GList *files = info->pairs;
2863 const GList *iter, *to_replace = NULL;
2865 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2866 SaveMimePartPair *pair = iter->data;
2867 if (modest_utils_file_exists (pair->filename)) {
2869 if (replaced_files == 1)
2873 if (replaced_files) {
2876 if (replaced_files == 1) {
2877 SaveMimePartPair *pair = to_replace->data;
2878 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2879 gchar *escaped_basename, *message;
2881 escaped_basename = g_uri_unescape_string (basename, NULL);
2882 message = g_strdup_printf ("%s\n%s",
2883 _FM("docm_nc_replace_file"),
2884 (escaped_basename) ? escaped_basename : "");
2885 response = modest_platform_run_confirmation_dialog (parent, message);
2887 g_free (escaped_basename);
2889 response = modest_platform_run_confirmation_dialog (parent,
2890 _FM("docm_nc_replace_multiple"));
2892 if (response != GTK_RESPONSE_OK)
2897 save_mime_part_info_free (info, TRUE);
2899 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2905 save_attachments_response (GtkDialog *dialog,
2909 TnyList *mime_parts;
2911 GList *files_to_save = NULL;
2912 gchar *current_folder;
2914 mime_parts = TNY_LIST (user_data);
2916 if (arg1 != GTK_RESPONSE_OK)
2919 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2920 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2921 if (current_folder && current_folder != '\0') {
2923 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2924 current_folder,&err);
2926 g_debug ("Error storing latest used folder: %s", err->message);
2930 g_free (current_folder);
2932 if (!modest_utils_folder_writable (chooser_uri)) {
2933 hildon_banner_show_information
2934 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2938 iter = tny_list_create_iterator (mime_parts);
2939 while (!tny_iterator_is_done (iter)) {
2940 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2942 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2943 !tny_mime_part_is_purged (mime_part) &&
2944 (tny_mime_part_get_filename (mime_part) != NULL)) {
2945 SaveMimePartPair *pair;
2947 pair = g_slice_new0 (SaveMimePartPair);
2949 if (tny_list_get_length (mime_parts) > 1) {
2951 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2952 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2955 pair->filename = g_strdup (chooser_uri);
2957 pair->part = mime_part;
2958 files_to_save = g_list_prepend (files_to_save, pair);
2960 tny_iterator_next (iter);
2962 g_object_unref (iter);
2964 g_free (chooser_uri);
2966 if (files_to_save != NULL) {
2967 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2968 info->pairs = files_to_save;
2969 info->result = TRUE;
2970 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
2974 /* Free and close the dialog */
2975 g_object_unref (mime_parts);
2976 gtk_widget_destroy (GTK_WIDGET (dialog));
2980 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2981 TnyList *mime_parts)
2983 ModestMsgViewWindowPrivate *priv;
2984 GtkWidget *save_dialog = NULL;
2985 gchar *conf_folder = NULL;
2986 gchar *filename = NULL;
2987 gchar *save_multiple_str = NULL;
2989 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2990 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2992 if (mime_parts == NULL) {
2993 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2994 * selection available */
2995 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2996 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2997 g_object_unref (mime_parts);
3000 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3002 g_object_unref (mime_parts);
3008 g_object_ref (mime_parts);
3011 /* prepare dialog */
3012 if (tny_list_get_length (mime_parts) == 1) {
3014 /* only one attachment selected */
3015 iter = tny_list_create_iterator (mime_parts);
3016 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3017 g_object_unref (iter);
3018 if (!modest_tny_mime_part_is_msg (mime_part) &&
3019 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3020 !tny_mime_part_is_purged (mime_part)) {
3021 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3023 /* TODO: show any error? */
3024 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3025 g_object_unref (mime_parts);
3028 g_object_unref (mime_part);
3030 gint num = tny_list_get_length (mime_parts);
3031 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3032 "sfil_va_number_of_objects_attachment",
3033 "sfil_va_number_of_objects_attachments",
3037 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3038 GTK_FILE_CHOOSER_ACTION_SAVE);
3041 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3042 if (conf_folder && conf_folder[0] != '\0') {
3043 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3046 /* Set the default folder to images folder */
3047 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3048 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3049 g_free (docs_folder);
3051 g_free (conf_folder);
3055 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3060 /* if multiple, set multiple string */
3061 if (save_multiple_str) {
3062 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3063 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3066 /* We must run this asynchronously, because the hildon dialog
3067 performs a gtk_dialog_run by itself which leads to gdk
3069 g_signal_connect (save_dialog, "response",
3070 G_CALLBACK (save_attachments_response), mime_parts);
3072 gtk_widget_show_all (save_dialog);
3076 show_remove_attachment_information (gpointer userdata)
3078 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3079 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3081 /* We're outside the main lock */
3082 gdk_threads_enter ();
3084 if (priv->remove_attachment_banner != NULL) {
3085 gtk_widget_destroy (priv->remove_attachment_banner);
3086 g_object_unref (priv->remove_attachment_banner);
3089 priv->remove_attachment_banner = g_object_ref (
3090 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3092 gdk_threads_leave ();
3098 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3100 ModestMsgViewWindowPrivate *priv;
3101 TnyList *mime_parts = NULL, *tmp;
3102 gchar *confirmation_message;
3108 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3109 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3111 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3112 * because we don't have selection
3114 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3116 /* Remove already purged messages from mime parts list. We use
3117 a copy of the list to remove items in the original one */
3118 tmp = tny_list_copy (mime_parts);
3119 iter = tny_list_create_iterator (tmp);
3120 while (!tny_iterator_is_done (iter)) {
3121 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3122 if (tny_mime_part_is_purged (part))
3123 tny_list_remove (mime_parts, (GObject *) part);
3125 g_object_unref (part);
3126 tny_iterator_next (iter);
3128 g_object_unref (tmp);
3129 g_object_unref (iter);
3131 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3132 tny_list_get_length (mime_parts) == 0) {
3133 g_object_unref (mime_parts);
3137 n_attachments = tny_list_get_length (mime_parts);
3138 if (n_attachments == 1) {
3142 iter = tny_list_create_iterator (mime_parts);
3143 part = (TnyMimePart *) tny_iterator_get_current (iter);
3144 g_object_unref (iter);
3145 if (modest_tny_mime_part_is_msg (part)) {
3147 header = tny_msg_get_header (TNY_MSG (part));
3148 filename = tny_header_dup_subject (header);
3149 g_object_unref (header);
3150 if (filename == NULL)
3151 filename = g_strdup (_("mail_va_no_subject"));
3153 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3155 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3157 g_object_unref (part);
3159 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3160 "mcen_nc_purge_files_text",
3161 n_attachments), n_attachments);
3163 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3164 confirmation_message);
3165 g_free (confirmation_message);
3167 if (response != GTK_RESPONSE_OK) {
3168 g_object_unref (mime_parts);
3172 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3174 iter = tny_list_create_iterator (mime_parts);
3175 while (!tny_iterator_is_done (iter)) {
3178 part = (TnyMimePart *) tny_iterator_get_current (iter);
3179 tny_mime_part_set_purged (TNY_MIME_PART (part));
3180 g_object_unref (part);
3181 tny_iterator_next (iter);
3183 g_object_unref (iter);
3185 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3186 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3187 tny_msg_rewrite_cache (msg);
3188 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3189 g_object_unref (msg);
3190 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3192 g_object_unref (mime_parts);
3194 if (priv->purge_timeout > 0) {
3195 g_source_remove (priv->purge_timeout);
3196 priv->purge_timeout = 0;
3199 if (priv->remove_attachment_banner) {
3200 gtk_widget_destroy (priv->remove_attachment_banner);
3201 g_object_unref (priv->remove_attachment_banner);
3202 priv->remove_attachment_banner = NULL;
3208 update_window_title (ModestMsgViewWindow *window)
3210 ModestMsgViewWindowPrivate *priv;
3212 TnyHeader *header = NULL;
3213 gchar *subject = NULL;
3215 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3217 /* Note that if the window is closed while we're retrieving
3218 the message, this widget could de deleted */
3219 if (!priv->msg_view)
3222 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3224 if (priv->other_body) {
3227 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3229 g_strstrip (description);
3230 subject = description;
3232 } else if (msg != NULL) {
3233 header = tny_msg_get_header (msg);
3234 subject = tny_header_dup_subject (header);
3235 g_object_unref (header);
3236 g_object_unref (msg);
3239 if ((subject == NULL)||(subject[0] == '\0')) {
3241 subject = g_strdup (_("mail_va_no_subject"));
3244 gtk_window_set_title (GTK_WINDOW (window), subject);
3249 on_move_focus (GtkWidget *widget,
3250 GtkDirectionType direction,
3253 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3257 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3259 GnomeVFSResult result;
3260 GnomeVFSHandle *handle = NULL;
3261 GnomeVFSFileInfo *info = NULL;
3264 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3265 if (result != GNOME_VFS_OK) {
3270 info = gnome_vfs_file_info_new ();
3271 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3272 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3273 /* We put a "safe" default size for going to cache */
3274 *expected_size = (300*1024);
3276 *expected_size = info->size;
3278 gnome_vfs_file_info_unref (info);
3280 stream = tny_vfs_stream_new (handle);
3289 TnyStream *output_stream;
3290 GtkWidget *msg_view;
3295 on_fetch_image_idle_refresh_view (gpointer userdata)
3298 FetchImageData *fidata = (FetchImageData *) userdata;
3300 gdk_threads_enter ();
3301 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3302 ModestMsgViewWindowPrivate *priv;
3304 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3305 priv->fetching_images--;
3306 gtk_widget_queue_draw (fidata->msg_view);
3307 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3309 gdk_threads_leave ();
3311 g_object_unref (fidata->msg_view);
3312 g_object_unref (fidata->window);
3313 g_slice_free (FetchImageData, fidata);
3318 on_fetch_image_thread (gpointer userdata)
3320 FetchImageData *fidata = (FetchImageData *) userdata;
3321 TnyStreamCache *cache;
3322 TnyStream *cache_stream;
3324 cache = modest_runtime_get_images_cache ();
3326 tny_stream_cache_get_stream (cache,
3328 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3329 (gpointer) fidata->uri);
3330 g_free (fidata->cache_id);
3331 g_free (fidata->uri);
3333 if (cache_stream != NULL) {
3336 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3339 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3340 if (G_UNLIKELY (nb_read < 0)) {
3342 } else if (G_LIKELY (nb_read > 0)) {
3343 gssize nb_written = 0;
3345 while (G_UNLIKELY (nb_written < nb_read)) {
3348 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3349 nb_read - nb_written);
3350 if (G_UNLIKELY (len < 0))
3356 tny_stream_close (cache_stream);
3357 g_object_unref (cache_stream);
3360 tny_stream_close (fidata->output_stream);
3361 g_object_unref (fidata->output_stream);
3363 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3369 on_fetch_image (ModestMsgView *msgview,
3372 ModestMsgViewWindow *window)
3374 const gchar *current_account;
3375 ModestMsgViewWindowPrivate *priv;
3376 FetchImageData *fidata;
3378 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3380 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3382 fidata = g_slice_new0 (FetchImageData);
3383 fidata->msg_view = g_object_ref (msgview);
3384 fidata->window = g_object_ref (window);
3385 fidata->uri = g_strdup (uri);
3386 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3387 fidata->output_stream = g_object_ref (stream);
3389 priv->fetching_images++;
3390 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3391 g_object_unref (fidata->output_stream);
3392 g_free (fidata->cache_id);
3393 g_free (fidata->uri);
3394 g_object_unref (fidata->msg_view);
3395 g_slice_free (FetchImageData, fidata);
3396 tny_stream_close (stream);
3397 priv->fetching_images--;
3398 update_progress_hint (window);
3401 update_progress_hint (window);
3407 setup_menu (ModestMsgViewWindow *self)
3409 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3411 /* Settings menu buttons */
3412 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3413 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3414 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3416 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_move_messages"), NULL,
3417 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3418 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3420 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3421 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3422 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3424 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3425 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3426 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3428 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3429 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3430 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3432 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3433 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3434 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3435 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3436 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3437 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3439 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3440 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3441 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3442 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3443 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3444 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3446 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3447 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3448 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3452 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3454 ModestMsgViewWindowPrivate *priv;
3455 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3456 GSList *recipients = NULL;
3458 gboolean contacts_to_add = FALSE;
3460 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3464 header = modest_msg_view_window_get_header (self);
3467 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3468 g_object_unref (header);
3470 recipients = modest_tny_msg_get_all_recipients_list (msg);
3471 g_object_unref (msg);
3474 if (recipients != NULL) {
3475 GtkWidget *picker_dialog;
3476 GtkWidget *selector;
3478 gchar *selected = NULL;
3480 selector = hildon_touch_selector_new_text ();
3481 g_object_ref (selector);
3483 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3484 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3485 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3486 (const gchar *) node->data);
3487 contacts_to_add = TRUE;
3491 if (contacts_to_add) {
3494 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3495 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3497 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3498 HILDON_TOUCH_SELECTOR (selector));
3500 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3502 if (picker_result == GTK_RESPONSE_OK) {
3503 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3505 gtk_widget_destroy (picker_dialog);
3508 modest_address_book_add_address (selected);
3513 g_object_unref (selector);
3518 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3522 _modest_msg_view_window_map_event (GtkWidget *widget,
3526 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3528 update_progress_hint (self);
3534 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3536 ModestMsgViewWindowPrivate *priv;
3537 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3539 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3543 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3545 ModestMsgViewWindowPrivate *priv;
3546 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3548 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3550 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3554 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3556 ModestMsgViewWindowPrivate *priv;
3559 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3561 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3562 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3564 if (!message_reader (self, priv, header, NULL, NULL, priv->row_reference)) {
3565 g_warning ("Shouldn't happen, trying to reload a message failed");
3568 g_object_unref (header);
3572 update_branding (ModestMsgViewWindow *self)
3574 const gchar *account;
3575 const gchar *mailbox;
3576 ModestAccountMgr *mgr;
3577 ModestProtocol *protocol = NULL;
3578 gchar *service_name = NULL;
3579 const GdkPixbuf *service_icon = NULL;
3580 ModestMsgViewWindowPrivate *priv;
3582 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3584 account = modest_window_get_active_account (MODEST_WINDOW (self));
3585 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3587 mgr = modest_runtime_get_account_mgr ();
3589 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3590 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3591 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3593 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3594 account, mailbox, MODEST_ICON_SIZE_SMALL);
3598 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3599 g_free (service_name);