1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
69 #include <modest-account-protocol.h>
71 #define MYDOCS_ENV "MYDOCSDIR"
72 #define DOCS_FOLDER ".documents"
74 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
75 struct _ModestMsgViewWindowPrivate {
78 GtkWidget *main_scroll;
79 GtkWidget *find_toolbar;
82 /* Progress observers */
83 GSList *progress_widgets;
86 GtkWidget *prev_toolitem;
87 GtkWidget *next_toolitem;
88 gboolean progress_hint;
91 /* Optimized view enabled */
92 gboolean optimized_view;
94 /* Whether this was created via the *_new_for_search_result() function. */
95 gboolean is_search_result;
97 /* Whether the message is in outbox */
100 /* A reference to the @model of the header view
101 * to allow selecting previous/next messages,
102 * if the message is currently selected in the header view.
104 const gchar *header_folder_id;
105 GtkTreeModel *header_model;
106 GtkTreeRowReference *row_reference;
107 GtkTreeRowReference *next_row_reference;
109 gulong clipboard_change_handler;
110 gulong queue_change_handler;
111 gulong account_removed_handler;
112 gulong row_changed_handler;
113 gulong row_deleted_handler;
114 gulong row_inserted_handler;
115 gulong rows_reordered_handler;
118 GtkWidget *remove_attachment_banner;
121 TnyMimePart *other_body;
126 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
127 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
128 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
129 static void modest_msg_view_window_finalize (GObject *obj);
130 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
131 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
132 ModestMsgViewWindow *obj);
133 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
134 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
137 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
139 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
140 static void modest_msg_view_window_set_zoom (ModestWindow *window,
142 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
143 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
144 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
147 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
149 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
150 gboolean show_toolbar);
152 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
154 ModestMsgViewWindow *window);
156 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
159 ModestMsgViewWindow *window);
161 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
163 ModestMsgViewWindow *window);
165 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
166 GtkTreePath *tree_path,
167 GtkTreeIter *tree_iter,
168 ModestMsgViewWindow *window);
170 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
174 ModestMsgViewWindow *window);
176 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
178 const gchar *tny_folder_id);
180 static void on_queue_changed (ModestMailOperationQueue *queue,
181 ModestMailOperation *mail_op,
182 ModestMailOperationQueueNotification type,
183 ModestMsgViewWindow *self);
185 static void on_account_removed (TnyAccountStore *account_store,
189 static void on_move_focus (GtkWidget *widget,
190 GtkDirectionType direction,
193 static void view_msg_cb (ModestMailOperation *mail_op,
200 static void set_progress_hint (ModestMsgViewWindow *self,
203 static void update_window_title (ModestMsgViewWindow *window);
205 static void init_window (ModestMsgViewWindow *obj);
207 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
209 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
211 static gboolean on_fetch_image (ModestMsgView *msgview,
214 ModestMsgViewWindow *window);
216 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
217 GtkScrollType scroll_type,
220 static gboolean message_reader (ModestMsgViewWindow *window,
221 ModestMsgViewWindowPrivate *priv,
223 const gchar *msg_uid,
225 GtkTreeRowReference *row_reference);
227 static void setup_menu (ModestMsgViewWindow *self);
228 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
231 static void update_branding (ModestMsgViewWindow *self);
234 /* list my signals */
241 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
242 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
245 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
246 MODEST_TYPE_MSG_VIEW_WINDOW, \
247 ModestMsgViewWindowPrivate))
249 static GtkWindowClass *parent_class = NULL;
251 /* uncomment the following if you have defined any signals */
252 static guint signals[LAST_SIGNAL] = {0};
255 modest_msg_view_window_get_type (void)
257 static GType my_type = 0;
259 static const GTypeInfo my_info = {
260 sizeof(ModestMsgViewWindowClass),
261 NULL, /* base init */
262 NULL, /* base finalize */
263 (GClassInitFunc) modest_msg_view_window_class_init,
264 NULL, /* class finalize */
265 NULL, /* class data */
266 sizeof(ModestMsgViewWindow),
268 (GInstanceInitFunc) modest_msg_view_window_init,
271 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
272 "ModestMsgViewWindow",
275 static const GInterfaceInfo modest_header_view_observer_info =
277 (GInterfaceInitFunc) modest_header_view_observer_init,
278 NULL, /* interface_finalize */
279 NULL /* interface_data */
282 g_type_add_interface_static (my_type,
283 MODEST_TYPE_HEADER_VIEW_OBSERVER,
284 &modest_header_view_observer_info);
290 save_state (ModestWindow *self)
292 modest_widget_memory_save (modest_runtime_get_conf (),
294 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
298 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
299 GtkScrollType scroll_type,
303 ModestMsgViewWindowPrivate *priv;
304 gboolean return_value;
306 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
307 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
312 add_scroll_binding (GtkBindingSet *binding_set,
314 GtkScrollType scroll)
316 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
318 gtk_binding_entry_add_signal (binding_set, keyval, 0,
320 GTK_TYPE_SCROLL_TYPE, scroll,
321 G_TYPE_BOOLEAN, FALSE);
322 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
324 GTK_TYPE_SCROLL_TYPE, scroll,
325 G_TYPE_BOOLEAN, FALSE);
329 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
331 GObjectClass *gobject_class;
332 HildonWindowClass *hildon_window_class;
333 ModestWindowClass *modest_window_class;
334 GtkBindingSet *binding_set;
336 gobject_class = (GObjectClass*) klass;
337 hildon_window_class = (HildonWindowClass *) klass;
338 modest_window_class = (ModestWindowClass *) klass;
340 parent_class = g_type_class_peek_parent (klass);
341 gobject_class->finalize = modest_msg_view_window_finalize;
343 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
344 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
345 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
346 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
347 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
348 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
350 modest_window_class->save_state_func = save_state;
352 klass->scroll_child = modest_msg_view_window_scroll_child;
354 signals[MSG_CHANGED_SIGNAL] =
355 g_signal_new ("msg-changed",
356 G_TYPE_FROM_CLASS (gobject_class),
358 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
360 modest_marshal_VOID__POINTER_POINTER,
361 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
363 signals[SCROLL_CHILD_SIGNAL] =
364 g_signal_new ("scroll-child",
365 G_TYPE_FROM_CLASS (gobject_class),
366 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
367 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
369 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
370 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
372 binding_set = gtk_binding_set_by_class (klass);
373 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
374 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
375 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
376 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
377 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
378 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
380 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
384 static void modest_header_view_observer_init(
385 ModestHeaderViewObserverIface *iface_class)
387 iface_class->update_func = modest_msg_view_window_update_model_replaced;
391 modest_msg_view_window_init (ModestMsgViewWindow *obj)
393 ModestMsgViewWindowPrivate *priv;
394 ModestWindowPrivate *parent_priv = NULL;
395 GtkActionGroup *action_group = NULL;
396 GError *error = NULL;
398 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
399 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
400 parent_priv->ui_manager = gtk_ui_manager_new();
402 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
403 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
405 /* Add common actions */
406 gtk_action_group_add_actions (action_group,
407 modest_action_entries,
408 G_N_ELEMENTS (modest_action_entries),
410 gtk_action_group_add_toggle_actions (action_group,
411 msg_view_toggle_action_entries,
412 G_N_ELEMENTS (msg_view_toggle_action_entries),
415 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
416 g_object_unref (action_group);
418 /* Load the UI definition */
419 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
422 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
423 g_error_free (error);
428 /* Add accelerators */
429 gtk_window_add_accel_group (GTK_WINDOW (obj),
430 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
432 priv->is_search_result = FALSE;
433 priv->is_outbox = FALSE;
435 priv->msg_view = NULL;
436 priv->header_model = NULL;
437 priv->header_folder_id = NULL;
438 priv->clipboard_change_handler = 0;
439 priv->queue_change_handler = 0;
440 priv->account_removed_handler = 0;
441 priv->row_changed_handler = 0;
442 priv->row_deleted_handler = 0;
443 priv->row_inserted_handler = 0;
444 priv->rows_reordered_handler = 0;
445 priv->progress_hint = FALSE;
446 priv->fetching_images = 0;
448 priv->optimized_view = FALSE;
449 priv->purge_timeout = 0;
450 priv->remove_attachment_banner = NULL;
451 priv->msg_uid = NULL;
452 priv->other_body = NULL;
454 priv->sighandlers = NULL;
457 init_window (MODEST_MSG_VIEW_WINDOW(obj));
459 hildon_program_add_window (hildon_program_get_instance(),
465 update_progress_hint (ModestMsgViewWindow *self)
467 ModestMsgViewWindowPrivate *priv;
468 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
470 if (GTK_WIDGET_VISIBLE (self)) {
471 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
472 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
477 set_progress_hint (ModestMsgViewWindow *self,
480 ModestWindowPrivate *parent_priv;
481 ModestMsgViewWindowPrivate *priv;
483 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
485 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
486 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
488 /* Sets current progress hint */
489 priv->progress_hint = enabled;
491 update_progress_hint (self);
497 init_window (ModestMsgViewWindow *obj)
499 GtkWidget *main_vbox;
500 ModestMsgViewWindowPrivate *priv;
502 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
504 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
505 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
506 main_vbox = gtk_vbox_new (FALSE, 6);
507 priv->main_scroll = hildon_pannable_area_new ();
508 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
509 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
510 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
512 /* NULL-ize fields if the window is destroyed */
513 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
515 gtk_widget_show_all (GTK_WIDGET(main_vbox));
519 modest_msg_view_window_disconnect_signals (ModestWindow *self)
521 ModestMsgViewWindowPrivate *priv;
522 GtkWidget *header_view = NULL;
523 GtkWindow *parent_window = NULL;
525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
527 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
528 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
529 priv->clipboard_change_handler))
530 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
531 priv->clipboard_change_handler);
533 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
534 priv->queue_change_handler))
535 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
536 priv->queue_change_handler);
538 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
539 priv->account_removed_handler))
540 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
541 priv->account_removed_handler);
543 if (priv->header_model) {
544 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
545 priv->row_changed_handler))
546 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
547 priv->row_changed_handler);
549 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
550 priv->row_deleted_handler))
551 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
552 priv->row_deleted_handler);
554 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
555 priv->row_inserted_handler))
556 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
557 priv->row_inserted_handler);
559 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
560 priv->rows_reordered_handler))
561 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
562 priv->rows_reordered_handler);
565 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
566 priv->sighandlers = NULL;
568 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
569 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
570 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
572 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
573 MODEST_HEADER_VIEW_OBSERVER(self));
579 modest_msg_view_window_finalize (GObject *obj)
581 ModestMsgViewWindowPrivate *priv;
583 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
585 /* Sanity check: shouldn't be needed, the window mgr should
586 call this function before */
587 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
589 if (priv->other_body != NULL) {
590 g_object_unref (priv->other_body);
591 priv->other_body = NULL;
594 if (priv->header_model != NULL) {
595 g_object_unref (priv->header_model);
596 priv->header_model = NULL;
599 if (priv->remove_attachment_banner) {
600 gtk_widget_destroy (priv->remove_attachment_banner);
601 g_object_unref (priv->remove_attachment_banner);
602 priv->remove_attachment_banner = NULL;
605 if (priv->purge_timeout > 0) {
606 g_source_remove (priv->purge_timeout);
607 priv->purge_timeout = 0;
610 if (priv->row_reference) {
611 gtk_tree_row_reference_free (priv->row_reference);
612 priv->row_reference = NULL;
615 if (priv->next_row_reference) {
616 gtk_tree_row_reference_free (priv->next_row_reference);
617 priv->next_row_reference = NULL;
621 g_free (priv->msg_uid);
622 priv->msg_uid = NULL;
625 G_OBJECT_CLASS(parent_class)->finalize (obj);
629 select_next_valid_row (GtkTreeModel *model,
630 GtkTreeRowReference **row_reference,
634 GtkTreeIter tmp_iter;
636 GtkTreePath *next = NULL;
637 gboolean retval = FALSE, finished;
639 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
641 path = gtk_tree_row_reference_get_path (*row_reference);
642 gtk_tree_model_get_iter (model, &tmp_iter, path);
643 gtk_tree_row_reference_free (*row_reference);
644 *row_reference = NULL;
648 TnyHeader *header = NULL;
650 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
651 gtk_tree_model_get (model, &tmp_iter,
652 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
656 if (msg_is_visible (header, is_outbox)) {
657 next = gtk_tree_model_get_path (model, &tmp_iter);
658 *row_reference = gtk_tree_row_reference_new (model, next);
659 gtk_tree_path_free (next);
663 g_object_unref (header);
666 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
667 next = gtk_tree_model_get_path (model, &tmp_iter);
669 /* Ensure that we are not selecting the same */
670 if (gtk_tree_path_compare (path, next) != 0) {
671 gtk_tree_model_get (model, &tmp_iter,
672 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
675 if (msg_is_visible (header, is_outbox)) {
676 *row_reference = gtk_tree_row_reference_new (model, next);
680 g_object_unref (header);
684 /* If we ended up in the same message
685 then there is no valid next
689 gtk_tree_path_free (next);
691 /* If there are no more messages and we don't
692 want to start again in the first one then
693 there is no valid next message */
699 gtk_tree_path_free (path);
704 /* TODO: This should be in _init(), with the parameters as properties. */
706 modest_msg_view_window_construct (ModestMsgViewWindow *self,
707 const gchar *modest_account_name,
708 const gchar *mailbox,
709 const gchar *msg_uid)
712 ModestMsgViewWindowPrivate *priv = NULL;
713 ModestWindowPrivate *parent_priv = NULL;
714 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
715 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
717 obj = G_OBJECT (self);
718 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
719 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
721 priv->msg_uid = g_strdup (msg_uid);
724 parent_priv->menubar = NULL;
726 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
727 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
730 /* Add common dimming rules */
731 modest_dimming_rules_group_add_rules (toolbar_rules_group,
732 modest_msg_view_toolbar_dimming_entries,
733 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
734 MODEST_WINDOW (self));
735 modest_dimming_rules_group_add_rules (clipboard_rules_group,
736 modest_msg_view_clipboard_dimming_entries,
737 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
738 MODEST_WINDOW (self));
740 /* Insert dimming rules group for this window */
741 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
742 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
743 g_object_unref (toolbar_rules_group);
744 g_object_unref (clipboard_rules_group);
746 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
748 priv->clipboard_change_handler = g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", G_CALLBACK (modest_msg_view_window_clipboard_owner_change), obj);
749 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
750 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
751 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
752 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
753 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
754 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
755 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
756 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
757 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
758 G_CALLBACK (modest_ui_actions_on_details), obj);
759 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
760 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
761 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
762 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
763 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
764 G_CALLBACK (on_fetch_image), obj);
766 g_signal_connect (G_OBJECT (obj), "key-release-event",
767 G_CALLBACK (modest_msg_view_window_key_event),
770 g_signal_connect (G_OBJECT (obj), "key-press-event",
771 G_CALLBACK (modest_msg_view_window_key_event),
774 g_signal_connect (G_OBJECT (obj), "move-focus",
775 G_CALLBACK (on_move_focus), obj);
777 g_signal_connect (G_OBJECT (obj), "map-event",
778 G_CALLBACK (_modest_msg_view_window_map_event),
781 /* Mail Operation Queue */
782 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
784 G_CALLBACK (on_queue_changed),
787 /* Account manager */
788 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
790 G_CALLBACK(on_account_removed),
793 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
794 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
796 /* First add out toolbar ... */
797 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
799 /* ... and later the find toolbar. This way find toolbar will
800 be shown over the other */
801 priv->find_toolbar = hildon_find_toolbar_new (NULL);
802 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
803 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
804 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
805 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
806 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
807 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
808 priv->last_search = NULL;
810 /* Init the clipboard actions dim status */
811 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
813 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
818 /* FIXME: parameter checks */
820 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
821 const gchar *modest_account_name,
822 const gchar *mailbox,
823 const gchar *msg_uid,
825 GtkTreeRowReference *row_reference)
827 ModestMsgViewWindow *window = NULL;
828 ModestMsgViewWindowPrivate *priv = NULL;
829 TnyFolder *header_folder = NULL;
830 ModestHeaderView *header_view = NULL;
831 ModestWindowMgr *mgr = NULL;
834 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
837 mgr = modest_runtime_get_window_mgr ();
838 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
839 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
841 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
843 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
845 /* Remember the message list's TreeModel so we can detect changes
846 * and change the list selection when necessary: */
847 header_folder = modest_header_view_get_folder (header_view);
849 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
850 TNY_FOLDER_TYPE_OUTBOX);
851 priv->header_folder_id = tny_folder_get_id (header_folder);
852 g_object_unref(header_folder);
855 /* Setup row references and connect signals */
856 priv->header_model = g_object_ref (model);
859 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
860 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
861 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
863 priv->row_reference = NULL;
864 priv->next_row_reference = NULL;
867 /* Connect signals */
868 priv->row_changed_handler =
869 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
870 G_CALLBACK(modest_msg_view_window_on_row_changed),
872 priv->row_deleted_handler =
873 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
874 G_CALLBACK(modest_msg_view_window_on_row_deleted),
876 priv->row_inserted_handler =
877 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
878 G_CALLBACK(modest_msg_view_window_on_row_inserted),
880 priv->rows_reordered_handler =
881 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
882 G_CALLBACK(modest_msg_view_window_on_row_reordered),
885 if (header_view != NULL){
886 modest_header_view_add_observer(header_view,
887 MODEST_HEADER_VIEW_OBSERVER(window));
890 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
891 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
892 update_branding (MODEST_MSG_VIEW_WINDOW (window));
894 /* gtk_widget_show_all (GTK_WIDGET (window)); */
895 modest_msg_view_window_update_priority (window);
896 /* Check dimming rules */
897 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
898 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
899 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
901 return MODEST_WINDOW(window);
905 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
906 const gchar *mailbox,
907 const gchar *msg_uid)
909 ModestMsgViewWindow *window = NULL;
910 ModestMsgViewWindowPrivate *priv = NULL;
911 ModestWindowMgr *mgr = NULL;
913 TnyAccount *account = NULL;
915 mgr = modest_runtime_get_window_mgr ();
916 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
917 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
919 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
921 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
925 is_merge = g_str_has_prefix (msg_uid, "merge:");
927 /* Get the account */
929 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
933 if (is_merge || account) {
934 TnyFolder *folder = NULL;
936 /* Try to get the message, if it's already downloaded
937 we don't need to connect */
939 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
941 ModestTnyAccountStore *account_store;
942 ModestTnyLocalFoldersAccount *local_folders_account;
944 account_store = modest_runtime_get_account_store ();
945 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
946 modest_tny_account_store_get_local_folders_account (account_store));
947 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
948 g_object_unref (local_folders_account);
952 gboolean device_online;
954 device = modest_runtime_get_device();
955 device_online = tny_device_is_online (device);
957 message_reader (window, priv, NULL, msg_uid, folder, NULL);
959 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
961 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
962 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
963 update_branding (MODEST_MSG_VIEW_WINDOW (window));
964 g_object_unref (msg);
966 message_reader (window, priv, NULL, msg_uid, folder, NULL);
969 g_object_unref (folder);
974 /* Check dimming rules */
975 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
976 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
977 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
979 return MODEST_WINDOW(window);
983 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
984 const gchar *modest_account_name,
985 const gchar *mailbox,
986 const gchar *msg_uid,
987 GtkTreeRowReference *row_reference)
989 ModestMsgViewWindow *window = NULL;
990 ModestMsgViewWindowPrivate *priv = NULL;
991 TnyFolder *header_folder = NULL;
992 ModestWindowMgr *mgr = NULL;
996 mgr = modest_runtime_get_window_mgr ();
997 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
998 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1000 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1002 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1004 /* Remember the message list's TreeModel so we can detect changes
1005 * and change the list selection when necessary: */
1007 if (header_view != NULL){
1008 header_folder = modest_header_view_get_folder(header_view);
1009 /* This could happen if the header folder was
1010 unseleted before opening this msg window (for
1011 example if the user selects an account in the
1012 folder view of the main window */
1013 if (header_folder) {
1014 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1015 TNY_FOLDER_TYPE_OUTBOX);
1016 priv->header_folder_id = tny_folder_get_id(header_folder);
1017 g_object_unref(header_folder);
1021 /* Setup row references and connect signals */
1022 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1023 g_object_ref (priv->header_model);
1025 if (row_reference) {
1026 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1027 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1028 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1030 priv->row_reference = NULL;
1031 priv->next_row_reference = NULL;
1034 /* Connect signals */
1035 priv->row_changed_handler =
1036 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1037 G_CALLBACK(modest_msg_view_window_on_row_changed),
1039 priv->row_deleted_handler =
1040 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1041 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1043 priv->row_inserted_handler =
1044 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1045 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1047 priv->rows_reordered_handler =
1048 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1049 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1052 if (header_view != NULL){
1053 modest_header_view_add_observer(header_view,
1054 MODEST_HEADER_VIEW_OBSERVER(window));
1057 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1058 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1060 path = gtk_tree_row_reference_get_path (row_reference);
1061 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1063 gtk_tree_model_get (priv->header_model, &iter,
1064 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1066 message_reader (window, priv, header, NULL, NULL, row_reference);
1067 g_object_unref (header);
1069 gtk_tree_path_free (path);
1071 /* Check dimming rules */
1072 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1073 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1074 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1076 return MODEST_WINDOW(window);
1080 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1081 const gchar *modest_account_name,
1082 const gchar *mailbox,
1083 const gchar *msg_uid)
1085 ModestMsgViewWindow *window = NULL;
1086 ModestMsgViewWindowPrivate *priv = NULL;
1087 ModestWindowMgr *mgr = NULL;
1089 mgr = modest_runtime_get_window_mgr ();
1090 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1091 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1092 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1094 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1096 /* Remember that this is a search result,
1097 * so we can disable some UI appropriately: */
1098 priv->is_search_result = TRUE;
1100 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1101 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1103 update_window_title (window);
1104 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1105 modest_msg_view_window_update_priority (window);
1107 /* Check dimming rules */
1108 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1109 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1110 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1112 return MODEST_WINDOW(window);
1116 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1118 ModestMsgViewWindowPrivate *priv = NULL;
1120 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1121 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1123 return (priv->other_body != NULL);
1127 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1128 TnyMimePart *other_body,
1129 const gchar *modest_account_name,
1130 const gchar *mailbox,
1131 const gchar *msg_uid)
1133 GObject *obj = NULL;
1134 ModestMsgViewWindowPrivate *priv;
1135 ModestWindowMgr *mgr = NULL;
1137 g_return_val_if_fail (msg, NULL);
1138 mgr = modest_runtime_get_window_mgr ();
1139 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1140 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1141 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1142 modest_account_name, mailbox, msg_uid);
1145 priv->other_body = g_object_ref (other_body);
1146 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1148 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1150 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1151 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1153 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1155 /* Check dimming rules */
1156 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1157 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1158 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1160 return MODEST_WINDOW(obj);
1164 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1165 const gchar *modest_account_name,
1166 const gchar *mailbox,
1167 const gchar *msg_uid)
1169 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1173 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1176 ModestMsgViewWindow *window)
1178 check_dimming_rules_after_change (window);
1182 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1184 ModestMsgViewWindow *window)
1186 check_dimming_rules_after_change (window);
1188 /* The window could have dissapeared */
1191 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1193 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1194 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1198 /* On insertions we check if the folder still has the message we are
1199 * showing or do not. If do not, we do nothing. Which means we are still
1200 * not attached to any header folder and thus next/prev buttons are
1201 * still dimmed. Once the message that is shown by msg-view is found, the
1202 * new model of header-view will be attached and the references will be set.
1203 * On each further insertions dimming rules will be checked. However
1204 * this requires extra CPU time at least works.
1205 * (An message might be deleted from TnyFolder and thus will not be
1206 * inserted into the model again for example if it is removed by the
1207 * imap server and the header view is refreshed.)
1210 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1211 GtkTreePath *tree_path,
1212 GtkTreeIter *tree_iter,
1213 ModestMsgViewWindow *window)
1215 ModestMsgViewWindowPrivate *priv = NULL;
1216 TnyHeader *header = NULL;
1218 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1219 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1221 g_assert (model == priv->header_model);
1223 /* Check if the newly inserted message is the same we are actually
1224 * showing. IF not, we should remain detached from the header model
1225 * and thus prev and next toolbar buttons should remain dimmed. */
1226 gtk_tree_model_get (model, tree_iter,
1227 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1230 if (TNY_IS_HEADER (header)) {
1233 uid = modest_tny_folder_get_header_unique_id (header);
1234 if (!g_str_equal(priv->msg_uid, uid)) {
1235 check_dimming_rules_after_change (window);
1237 g_object_unref (G_OBJECT(header));
1241 g_object_unref(G_OBJECT(header));
1244 if (priv->row_reference) {
1245 gtk_tree_row_reference_free (priv->row_reference);
1248 /* Setup row_reference for the actual msg. */
1249 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1250 if (priv->row_reference == NULL) {
1251 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1255 /* Now set up next_row_reference. */
1256 if (priv->next_row_reference) {
1257 gtk_tree_row_reference_free (priv->next_row_reference);
1260 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1261 select_next_valid_row (priv->header_model,
1262 &(priv->next_row_reference), FALSE, priv->is_outbox);
1264 /* Connect the remaining callbacks to become able to detect
1265 * changes in header-view. */
1266 priv->row_changed_handler =
1267 g_signal_connect (priv->header_model, "row-changed",
1268 G_CALLBACK (modest_msg_view_window_on_row_changed),
1270 priv->row_deleted_handler =
1271 g_signal_connect (priv->header_model, "row-deleted",
1272 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1274 priv->rows_reordered_handler =
1275 g_signal_connect (priv->header_model, "rows-reordered",
1276 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1279 check_dimming_rules_after_change (window);
1283 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1287 ModestMsgViewWindow *window)
1289 ModestMsgViewWindowPrivate *priv = NULL;
1290 gboolean already_changed = FALSE;
1292 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1294 /* If the current row was reordered select the proper next
1295 valid row. The same if the next row reference changes */
1296 if (!priv->row_reference ||
1297 !gtk_tree_row_reference_valid (priv->row_reference))
1300 if (priv->next_row_reference &&
1301 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1302 GtkTreePath *cur, *next;
1303 /* Check that the order is still the correct one */
1304 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1305 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1306 gtk_tree_path_next (cur);
1307 if (gtk_tree_path_compare (cur, next) != 0) {
1308 gtk_tree_row_reference_free (priv->next_row_reference);
1309 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1310 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1311 already_changed = TRUE;
1313 gtk_tree_path_free (cur);
1314 gtk_tree_path_free (next);
1316 if (priv->next_row_reference)
1317 gtk_tree_row_reference_free (priv->next_row_reference);
1318 /* Update next row reference */
1319 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1320 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1321 already_changed = TRUE;
1324 check_dimming_rules_after_change (window);
1327 /* The modest_msg_view_window_update_model_replaced implements update
1328 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1329 * actually belongs to the header-view is the same as the TnyFolder of
1330 * the message of msg-view or not. If they are different, there is
1331 * nothing to do. If they are the same, then the model has replaced and
1332 * the reference in msg-view shall be replaced from the old model to
1333 * the new model. In this case the view will be detached from it's
1334 * header folder. From this point the next/prev buttons are dimmed.
1337 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1338 GtkTreeModel *model,
1339 const gchar *tny_folder_id)
1341 ModestMsgViewWindowPrivate *priv = NULL;
1342 ModestMsgViewWindow *window = NULL;
1344 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1345 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1347 window = MODEST_MSG_VIEW_WINDOW(observer);
1348 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1350 /* If there is an other folder in the header-view then we do
1351 * not care about it's model (msg list). Else if the
1352 * header-view shows the folder the msg shown by us is in, we
1353 * shall replace our model reference and make some check. */
1354 if(model == NULL || tny_folder_id == NULL ||
1355 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1358 /* Model is changed(replaced), so we should forget the old
1359 * one. Because there might be other references and there
1360 * might be some change on the model even if we unreferenced
1361 * it, we need to disconnect our signals here. */
1362 if (priv->header_model) {
1363 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1364 priv->row_changed_handler))
1365 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1366 priv->row_changed_handler);
1367 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1368 priv->row_deleted_handler))
1369 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1370 priv->row_deleted_handler);
1371 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1372 priv->row_inserted_handler))
1373 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1374 priv->row_inserted_handler);
1375 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1376 priv->rows_reordered_handler))
1377 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1378 priv->rows_reordered_handler);
1381 if (priv->row_reference)
1382 gtk_tree_row_reference_free (priv->row_reference);
1383 if (priv->next_row_reference)
1384 gtk_tree_row_reference_free (priv->next_row_reference);
1385 g_object_unref(priv->header_model);
1388 priv->row_changed_handler = 0;
1389 priv->row_deleted_handler = 0;
1390 priv->row_inserted_handler = 0;
1391 priv->rows_reordered_handler = 0;
1392 priv->next_row_reference = NULL;
1393 priv->row_reference = NULL;
1394 priv->header_model = NULL;
1397 priv->header_model = g_object_ref (model);
1399 /* Also we must connect to the new model for row insertions.
1400 * Only for insertions now. We will need other ones only after
1401 * the msg is show by msg-view is added to the new model. */
1402 priv->row_inserted_handler =
1403 g_signal_connect (priv->header_model, "row-inserted",
1404 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1407 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1408 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1412 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1414 ModestMsgViewWindowPrivate *priv= NULL;
1416 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1417 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1419 return priv->progress_hint;
1423 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1425 ModestMsgViewWindowPrivate *priv= NULL;
1427 TnyHeader *header = NULL;
1428 GtkTreePath *path = NULL;
1431 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1432 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1434 /* If the message was not obtained from a treemodel,
1435 * for instance if it was opened directly by the search UI:
1437 if (priv->header_model == NULL ||
1438 priv->row_reference == NULL ||
1439 !gtk_tree_row_reference_valid (priv->row_reference)) {
1440 msg = modest_msg_view_window_get_message (self);
1442 header = tny_msg_get_header (msg);
1443 g_object_unref (msg);
1448 /* Get iter of the currently selected message in the header view: */
1449 path = gtk_tree_row_reference_get_path (priv->row_reference);
1450 g_return_val_if_fail (path != NULL, NULL);
1451 gtk_tree_model_get_iter (priv->header_model,
1455 /* Get current message header */
1456 gtk_tree_model_get (priv->header_model, &iter,
1457 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1460 gtk_tree_path_free (path);
1465 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1467 ModestMsgViewWindowPrivate *priv;
1469 g_return_val_if_fail (self, NULL);
1471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1473 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1477 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1479 ModestMsgViewWindowPrivate *priv;
1481 g_return_val_if_fail (self, NULL);
1483 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1485 return (const gchar*) priv->msg_uid;
1488 /* Used for the Ctrl+F accelerator */
1490 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1493 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1494 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1496 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1497 modest_msg_view_window_find_toolbar_close (obj, data);
1499 modest_msg_view_window_show_find_toolbar (obj, data);
1503 /* Handler for menu option */
1505 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1508 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1509 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1511 gtk_widget_show (priv->find_toolbar);
1512 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1515 /* Handler for click on the "X" close button in find toolbar */
1517 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1518 ModestMsgViewWindow *obj)
1520 ModestMsgViewWindowPrivate *priv;
1522 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1525 gtk_widget_hide (priv->find_toolbar);
1526 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1530 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1531 ModestMsgViewWindow *obj)
1533 gchar *current_search;
1534 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1536 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1537 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1541 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1543 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1544 g_free (current_search);
1545 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1549 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1551 g_free (priv->last_search);
1552 priv->last_search = g_strdup (current_search);
1553 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1556 hildon_banner_show_information (NULL, NULL,
1557 _HL("ckct_ib_find_no_matches"));
1558 g_free (priv->last_search);
1559 priv->last_search = NULL;
1561 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1564 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1565 hildon_banner_show_information (NULL, NULL,
1566 _HL("ckct_ib_find_search_complete"));
1567 g_free (priv->last_search);
1568 priv->last_search = NULL;
1570 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1574 g_free (current_search);
1579 modest_msg_view_window_set_zoom (ModestWindow *window,
1582 ModestMsgViewWindowPrivate *priv;
1583 ModestWindowPrivate *parent_priv;
1585 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1587 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1588 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1589 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1594 modest_msg_view_window_get_zoom (ModestWindow *window)
1596 ModestMsgViewWindowPrivate *priv;
1598 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1600 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1601 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1605 modest_msg_view_window_zoom_plus (ModestWindow *window)
1608 ModestMsgViewWindowPrivate *priv;
1612 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1613 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1615 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1617 if (zoom_level >= 2.0) {
1618 hildon_banner_show_information (NULL, NULL,
1619 _CS("ckct_ib_max_zoom_level_reached"));
1621 } else if (zoom_level >= 1.5) {
1623 } else if (zoom_level >= 1.2) {
1625 } else if (zoom_level >= 1.0) {
1627 } else if (zoom_level >= 0.8) {
1629 } else if (zoom_level >= 0.5) {
1635 /* set zoom level */
1636 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1637 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1638 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1639 g_free (banner_text);
1640 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1646 modest_msg_view_window_zoom_minus (ModestWindow *window)
1649 ModestMsgViewWindowPrivate *priv;
1653 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1654 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1656 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1658 if (zoom_level <= 0.5) {
1659 hildon_banner_show_information (NULL, NULL,
1660 _CS("ckct_ib_min_zoom_level_reached"));
1662 } else if (zoom_level <= 0.8) {
1664 } else if (zoom_level <= 1.0) {
1666 } else if (zoom_level <= 1.2) {
1668 } else if (zoom_level <= 1.5) {
1670 } else if (zoom_level <= 2.0) {
1676 /* set zoom level */
1677 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1678 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1679 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1680 g_free (banner_text);
1681 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1688 modest_msg_view_window_key_event (GtkWidget *window,
1694 focus = gtk_window_get_focus (GTK_WINDOW (window));
1696 /* for the find toolbar case */
1697 if (focus && GTK_IS_ENTRY (focus)) {
1698 if (event->keyval == GDK_BackSpace) {
1700 copy = gdk_event_copy ((GdkEvent *) event);
1701 gtk_widget_event (focus, copy);
1702 gdk_event_free (copy);
1707 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1708 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1709 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1710 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1711 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1712 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1713 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1714 /* gboolean return_value; */
1716 if (event->type == GDK_KEY_PRESS) {
1717 GtkScrollType scroll_type;
1719 switch (event->keyval) {
1722 scroll_type = GTK_SCROLL_STEP_UP; break;
1725 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1727 case GDK_KP_Page_Up:
1728 scroll_type = GTK_SCROLL_PAGE_UP; break;
1730 case GDK_KP_Page_Down:
1731 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1734 scroll_type = GTK_SCROLL_START; break;
1737 scroll_type = GTK_SCROLL_END; break;
1738 default: scroll_type = GTK_SCROLL_NONE;
1741 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1742 /* scroll_type, FALSE, &return_value); */
1753 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1756 ModestMsgViewWindowPrivate *priv;
1757 GtkTreeIter tmp_iter;
1758 gboolean is_last_selected;
1760 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1761 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1763 /*if no model (so no rows at all), then virtually we are the last*/
1764 if (!priv->header_model || !priv->row_reference)
1767 if (!gtk_tree_row_reference_valid (priv->row_reference))
1770 path = gtk_tree_row_reference_get_path (priv->row_reference);
1774 is_last_selected = TRUE;
1775 while (is_last_selected) {
1777 gtk_tree_path_next (path);
1778 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1780 gtk_tree_model_get (priv->header_model, &tmp_iter,
1781 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1784 if (msg_is_visible (header, priv->is_outbox))
1785 is_last_selected = FALSE;
1786 g_object_unref(G_OBJECT(header));
1789 gtk_tree_path_free (path);
1790 return is_last_selected;
1794 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1796 ModestMsgViewWindowPrivate *priv;
1798 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1799 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1801 return priv->header_model != NULL;
1805 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1807 ModestMsgViewWindowPrivate *priv;
1809 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1810 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1812 return priv->is_search_result;
1816 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1818 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1820 if (!check_outbox) {
1823 ModestTnySendQueueStatus status;
1824 status = modest_tny_all_send_queues_get_msg_status (header);
1825 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1826 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1831 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1834 ModestMsgViewWindowPrivate *priv;
1835 gboolean is_first_selected;
1836 GtkTreeIter tmp_iter;
1838 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1839 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1841 /*if no model (so no rows at all), then virtually we are the first*/
1842 if (!priv->header_model || !priv->row_reference)
1845 if (!gtk_tree_row_reference_valid (priv->row_reference))
1848 path = gtk_tree_row_reference_get_path (priv->row_reference);
1852 is_first_selected = TRUE;
1853 while (is_first_selected) {
1855 if(!gtk_tree_path_prev (path))
1857 /* Here the 'if' is needless for logic, but let make sure
1858 * iter is valid for gtk_tree_model_get. */
1859 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1861 gtk_tree_model_get (priv->header_model, &tmp_iter,
1862 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1865 if (msg_is_visible (header, priv->is_outbox))
1866 is_first_selected = FALSE;
1867 g_object_unref(G_OBJECT(header));
1870 gtk_tree_path_free (path);
1871 return is_first_selected;
1878 GtkTreeRowReference *row_reference;
1882 message_reader_performer (gboolean canceled,
1884 GtkWindow *parent_window,
1885 TnyAccount *account,
1888 ModestMailOperation *mail_op = NULL;
1889 MsgReaderInfo *info;
1891 info = (MsgReaderInfo *) user_data;
1892 if (canceled || err) {
1893 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1897 /* Register the header - it'll be unregistered in the callback */
1899 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1901 /* New mail operation */
1902 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1903 modest_ui_actions_disk_operations_error_handler,
1906 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1908 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1910 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1911 g_object_unref (mail_op);
1913 /* Update dimming rules */
1914 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1915 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1918 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1919 g_free (info->msg_uid);
1921 g_object_unref (info->folder);
1923 g_object_unref (info->header);
1924 g_slice_free (MsgReaderInfo, info);
1929 * Reads the message whose summary item is @header. It takes care of
1930 * several things, among others:
1932 * If the message was not previously downloaded then ask the user
1933 * before downloading. If there is no connection launch the connection
1934 * dialog. Update toolbar dimming rules.
1936 * Returns: TRUE if the mail operation was started, otherwise if the
1937 * user do not want to download the message, or if the user do not
1938 * want to connect, then the operation is not issued
1941 message_reader (ModestMsgViewWindow *window,
1942 ModestMsgViewWindowPrivate *priv,
1944 const gchar *msg_uid,
1946 GtkTreeRowReference *row_reference)
1948 ModestWindowMgr *mgr;
1949 TnyAccount *account;
1950 MsgReaderInfo *info;
1952 /* We set the header from model while we're loading */
1953 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1954 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1957 g_object_ref (folder);
1959 mgr = modest_runtime_get_window_mgr ();
1960 /* Msg download completed */
1961 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1963 /* Ask the user if he wants to download the message if
1965 if (!tny_device_is_online (modest_runtime_get_device())) {
1966 GtkResponseType response;
1968 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1969 _("mcen_nc_get_msg"));
1970 if (response == GTK_RESPONSE_CANCEL) {
1971 update_window_title (window);
1976 folder = tny_header_get_folder (header);
1978 info = g_slice_new (MsgReaderInfo);
1979 info->msg_uid = g_strdup (msg_uid);
1981 info->header = g_object_ref (header);
1983 info->header = NULL;
1985 info->folder = g_object_ref (folder);
1987 info->folder = NULL;
1988 if (row_reference) {
1989 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1991 info->row_reference = NULL;
1994 /* Offer the connection dialog if necessary */
1995 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1997 TNY_FOLDER_STORE (folder),
1998 message_reader_performer,
2000 g_object_unref (folder);
2006 folder = tny_header_get_folder (header);
2008 account = tny_folder_get_account (folder);
2009 info = g_slice_new (MsgReaderInfo);
2010 info->msg_uid = g_strdup (msg_uid);
2012 info->folder = g_object_ref (folder);
2014 info->folder = NULL;
2016 info->header = g_object_ref (header);
2018 info->header = NULL;
2020 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2022 info->row_reference = NULL;
2024 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2025 g_object_unref (account);
2026 g_object_unref (folder);
2032 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2034 ModestMsgViewWindowPrivate *priv;
2035 GtkTreePath *path= NULL;
2036 GtkTreeIter tmp_iter;
2038 gboolean retval = TRUE;
2039 GtkTreeRowReference *row_reference = NULL;
2041 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2042 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2044 if (!priv->row_reference)
2047 /* Update the next row reference if it's not valid. This could
2048 happen if for example the header which it was pointing to,
2049 was deleted. The best place to do it is in the row-deleted
2050 handler but the tinymail model do not work like the glib
2051 tree models and reports the deletion when the row is still
2053 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2054 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2055 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2056 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2059 if (priv->next_row_reference)
2060 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2064 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2066 gtk_tree_model_get_iter (priv->header_model,
2069 gtk_tree_path_free (path);
2071 gtk_tree_model_get (priv->header_model, &tmp_iter,
2072 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2075 /* Read the message & show it */
2076 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2079 gtk_tree_row_reference_free (row_reference);
2082 g_object_unref (header);
2088 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2090 ModestMsgViewWindowPrivate *priv = NULL;
2092 gboolean finished = FALSE;
2093 gboolean retval = FALSE;
2095 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2096 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2098 /* Return inmediatly if there is no header model */
2099 if (!priv->header_model || !priv->row_reference)
2102 path = gtk_tree_row_reference_get_path (priv->row_reference);
2103 while (!finished && gtk_tree_path_prev (path)) {
2107 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2108 gtk_tree_model_get (priv->header_model, &iter,
2109 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2113 if (msg_is_visible (header, priv->is_outbox)) {
2114 GtkTreeRowReference *row_reference;
2115 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2116 /* Read the message & show it */
2117 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2118 gtk_tree_row_reference_free (row_reference);
2122 g_object_unref (header);
2126 gtk_tree_path_free (path);
2131 view_msg_cb (ModestMailOperation *mail_op,
2138 ModestMsgViewWindow *self = NULL;
2139 ModestMsgViewWindowPrivate *priv = NULL;
2140 GtkTreeRowReference *row_reference = NULL;
2142 /* Unregister the header (it was registered before creating the mail operation) */
2143 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2145 row_reference = (GtkTreeRowReference *) user_data;
2148 gtk_tree_row_reference_free (row_reference);
2149 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2151 /* Restore window title */
2152 update_window_title (self);
2153 g_object_unref (self);
2158 /* If there was any error */
2159 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2161 gtk_tree_row_reference_free (row_reference);
2162 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2164 /* Restore window title */
2165 update_window_title (self);
2166 g_object_unref (self);
2171 /* Get the window */
2172 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2173 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2174 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2176 /* Update the row reference */
2177 if (priv->row_reference != NULL) {
2178 gtk_tree_row_reference_free (priv->row_reference);
2179 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2180 if (priv->next_row_reference != NULL) {
2181 gtk_tree_row_reference_free (priv->next_row_reference);
2183 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2184 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2187 /* Mark header as read */
2188 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2189 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2191 /* Set new message */
2192 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2193 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2194 modest_msg_view_window_update_priority (self);
2195 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2196 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2197 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2200 /* Set the new message uid of the window */
2201 if (priv->msg_uid) {
2202 g_free (priv->msg_uid);
2203 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2206 /* Notify the observers */
2207 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2208 0, priv->header_model, priv->row_reference);
2211 g_object_unref (self);
2213 gtk_tree_row_reference_free (row_reference);
2217 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2219 ModestMsgViewWindowPrivate *priv;
2221 TnyFolderType folder_type;
2223 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2225 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2227 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2231 folder = tny_msg_get_folder (msg);
2233 folder_type = modest_tny_folder_guess_folder_type (folder);
2234 g_object_unref (folder);
2236 g_object_unref (msg);
2244 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2246 ModestMsgViewWindowPrivate *priv;
2247 TnyHeader *header = NULL;
2248 TnyHeaderFlags flags = 0;
2250 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2252 if (priv->header_model && priv->row_reference) {
2254 GtkTreePath *path = NULL;
2256 path = gtk_tree_row_reference_get_path (priv->row_reference);
2257 g_return_if_fail (path != NULL);
2258 gtk_tree_model_get_iter (priv->header_model,
2260 gtk_tree_row_reference_get_path (priv->row_reference));
2262 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2264 gtk_tree_path_free (path);
2267 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2269 header = tny_msg_get_header (msg);
2270 g_object_unref (msg);
2275 flags = tny_header_get_flags (header);
2276 g_object_unref(G_OBJECT(header));
2279 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2284 toolbar_resize (ModestMsgViewWindow *self)
2286 ModestMsgViewWindowPrivate *priv = NULL;
2287 ModestWindowPrivate *parent_priv = NULL;
2289 gint static_button_size;
2290 ModestWindowMgr *mgr;
2292 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2293 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2294 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2296 mgr = modest_runtime_get_window_mgr ();
2297 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2299 if (parent_priv->toolbar) {
2300 /* left size buttons */
2301 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2302 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2303 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2304 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2305 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2306 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2307 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2308 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2309 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2310 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2311 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2312 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2313 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2314 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2315 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2316 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2318 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2319 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2320 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2321 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2326 modest_msg_view_window_show_toolbar (ModestWindow *self,
2327 gboolean show_toolbar)
2329 ModestMsgViewWindowPrivate *priv = NULL;
2330 ModestWindowPrivate *parent_priv;
2332 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2333 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2335 /* Set optimized view status */
2336 priv->optimized_view = !show_toolbar;
2338 if (!parent_priv->toolbar) {
2339 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2341 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2342 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2344 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2345 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2346 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2349 hildon_window_add_toolbar (HILDON_WINDOW (self),
2350 GTK_TOOLBAR (parent_priv->toolbar));
2355 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2356 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2357 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2359 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2360 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2361 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2363 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2366 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2367 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2372 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2374 ModestMsgViewWindow *window)
2376 if (!GTK_WIDGET_VISIBLE (window))
2379 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2383 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2385 ModestMsgViewWindowPrivate *priv;
2387 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2388 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2390 return priv->progress_hint;
2394 observers_empty (ModestMsgViewWindow *self)
2397 ModestMsgViewWindowPrivate *priv;
2398 gboolean is_empty = TRUE;
2399 guint pending_ops = 0;
2401 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2402 tmp = priv->progress_widgets;
2404 /* Check all observers */
2405 while (tmp && is_empty) {
2406 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2407 is_empty = pending_ops == 0;
2409 tmp = g_slist_next(tmp);
2416 on_account_removed (TnyAccountStore *account_store,
2417 TnyAccount *account,
2420 /* Do nothing if it's a transport account, because we only
2421 show the messages of a store account */
2422 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2423 const gchar *parent_acc = NULL;
2424 const gchar *our_acc = NULL;
2426 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2427 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2429 /* Close this window if I'm showing a message of the removed account */
2430 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2431 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2436 on_mail_operation_started (ModestMailOperation *mail_op,
2439 ModestMsgViewWindow *self;
2440 ModestMailOperationTypeOperation op_type;
2442 ModestMsgViewWindowPrivate *priv;
2443 GObject *source = NULL;
2445 self = MODEST_MSG_VIEW_WINDOW (user_data);
2446 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2447 op_type = modest_mail_operation_get_type_operation (mail_op);
2448 tmp = priv->progress_widgets;
2449 source = modest_mail_operation_get_source(mail_op);
2450 if (G_OBJECT (self) == source) {
2451 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2452 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2453 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2454 set_progress_hint (self, TRUE);
2456 modest_progress_object_add_operation (
2457 MODEST_PROGRESS_OBJECT (tmp->data),
2459 tmp = g_slist_next (tmp);
2463 g_object_unref (source);
2465 /* Update dimming rules */
2466 check_dimming_rules_after_change (self);
2470 on_mail_operation_finished (ModestMailOperation *mail_op,
2473 ModestMsgViewWindow *self;
2474 ModestMailOperationTypeOperation op_type;
2476 ModestMsgViewWindowPrivate *priv;
2478 self = MODEST_MSG_VIEW_WINDOW (user_data);
2479 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2480 op_type = modest_mail_operation_get_type_operation (mail_op);
2481 tmp = priv->progress_widgets;
2483 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2484 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2485 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2487 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2489 tmp = g_slist_next (tmp);
2492 /* If no more operations are being observed, NORMAL mode is enabled again */
2493 if (observers_empty (self)) {
2494 set_progress_hint (self, FALSE);
2498 /* Update dimming rules. We have to do this right here
2499 and not in view_msg_cb because at that point the
2500 transfer mode is still enabled so the dimming rule
2501 won't let the user delete the message that has been
2502 readed for example */
2503 check_dimming_rules_after_change (self);
2507 on_queue_changed (ModestMailOperationQueue *queue,
2508 ModestMailOperation *mail_op,
2509 ModestMailOperationQueueNotification type,
2510 ModestMsgViewWindow *self)
2512 ModestMsgViewWindowPrivate *priv;
2514 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2516 /* If this operations was created by another window, do nothing */
2517 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2520 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2521 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2523 "operation-started",
2524 G_CALLBACK (on_mail_operation_started),
2526 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2528 "operation-finished",
2529 G_CALLBACK (on_mail_operation_finished),
2531 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2532 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2534 "operation-started");
2535 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2537 "operation-finished");
2542 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2544 ModestMsgViewWindowPrivate *priv;
2545 TnyList *selected_attachments = NULL;
2547 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2548 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2550 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2551 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2553 return selected_attachments;
2557 ModestMsgViewWindow *self;
2559 } DecodeAsyncHelper;
2562 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2568 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2570 /* It could happen that the window was closed */
2571 if (GTK_WIDGET_VISIBLE (helper->self))
2572 set_progress_hint (helper->self, FALSE);
2574 if (cancelled || err) {
2576 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2577 modest_platform_information_banner (NULL, NULL, msg);
2583 /* make the file read-only */
2584 g_chmod(helper->file_path, 0444);
2586 /* Activate the file */
2587 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2591 g_object_unref (helper->self);
2592 g_free (helper->file_path);
2593 g_slice_free (DecodeAsyncHelper, helper);
2597 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2598 TnyMimePart *mime_part)
2600 ModestMsgViewWindowPrivate *priv;
2601 const gchar *msg_uid;
2602 gchar *attachment_uid = NULL;
2603 gint attachment_index = 0;
2604 TnyList *attachments;
2606 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2607 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2608 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2610 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2611 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2612 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2613 g_object_unref (attachments);
2615 if (msg_uid && attachment_index >= 0) {
2616 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2619 if (mime_part == NULL) {
2620 gboolean error = FALSE;
2621 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2622 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2624 } else if (tny_list_get_length (selected_attachments) > 1) {
2625 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2629 iter = tny_list_create_iterator (selected_attachments);
2630 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2631 g_object_unref (iter);
2633 if (selected_attachments)
2634 g_object_unref (selected_attachments);
2639 g_object_ref (mime_part);
2642 if (tny_mime_part_is_purged (mime_part))
2645 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2646 gchar *filepath = NULL;
2647 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2648 gboolean show_error_banner = FALSE;
2649 TnyFsStream *temp_stream = NULL;
2650 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2653 if (temp_stream != NULL) {
2654 DecodeAsyncHelper *helper;
2656 /* Activate progress hint */
2657 set_progress_hint (window, TRUE);
2659 helper = g_slice_new0 (DecodeAsyncHelper);
2660 helper->self = g_object_ref (window);
2661 helper->file_path = g_strdup (filepath);
2663 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2664 on_decode_to_stream_async_handler,
2667 g_object_unref (temp_stream);
2668 /* NOTE: files in the temporary area will be automatically
2669 * cleaned after some time if they are no longer in use */
2672 const gchar *content_type;
2673 /* the file may already exist but it isn't writable,
2674 * let's try to open it anyway */
2675 content_type = tny_mime_part_get_content_type (mime_part);
2676 modest_platform_activate_file (filepath, content_type);
2678 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2679 show_error_banner = TRUE;
2684 if (show_error_banner)
2685 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2686 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2687 ModestWindowMgr *mgr;
2688 ModestWindow *msg_win = NULL;
2689 TnyMsg *current_msg;
2693 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2694 mgr = modest_runtime_get_window_mgr ();
2695 header = tny_msg_get_header (TNY_MSG (current_msg));
2696 found = modest_window_mgr_find_registered_message_uid (mgr,
2701 g_debug ("window for this body is already being created");
2704 /* it's not found, so create a new window for it */
2705 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2706 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2707 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2709 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2711 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2712 account, mailbox, attachment_uid);
2714 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2715 modest_window_get_zoom (MODEST_WINDOW (window)));
2716 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2717 gtk_widget_show_all (GTK_WIDGET (msg_win));
2719 gtk_widget_destroy (GTK_WIDGET (msg_win));
2721 g_object_unref (current_msg);
2723 /* message attachment */
2724 TnyHeader *header = NULL;
2725 ModestWindowMgr *mgr;
2726 ModestWindow *msg_win = NULL;
2729 header = tny_msg_get_header (TNY_MSG (mime_part));
2730 mgr = modest_runtime_get_window_mgr ();
2731 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2734 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2735 * thus, we don't do anything */
2736 g_debug ("window for is already being created");
2738 /* it's not found, so create a new window for it */
2739 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2740 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2741 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2743 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2744 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2745 mailbox, attachment_uid);
2746 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2747 modest_window_get_zoom (MODEST_WINDOW (window)));
2748 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2749 gtk_widget_show_all (GTK_WIDGET (msg_win));
2751 gtk_widget_destroy (GTK_WIDGET (msg_win));
2757 g_free (attachment_uid);
2759 g_object_unref (mime_part);
2771 GnomeVFSResult result;
2774 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2775 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2776 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2777 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2780 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2784 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2785 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2786 g_free (pair->filename);
2787 g_object_unref (pair->part);
2788 g_slice_free (SaveMimePartPair, pair);
2790 g_list_free (info->pairs);
2793 g_slice_free (SaveMimePartInfo, info);
2798 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2800 /* This is a GDK lock because we are an idle callback and
2801 * hildon_banner_show_information is or does Gtk+ code */
2803 gdk_threads_enter (); /* CHECKED */
2804 if (info->result == GNOME_VFS_OK) {
2805 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2806 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2807 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2808 modest_platform_information_banner (NULL, NULL, msg);
2811 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2813 save_mime_part_info_free (info, FALSE);
2814 gdk_threads_leave (); /* CHECKED */
2820 save_mime_part_to_file (SaveMimePartInfo *info)
2822 GnomeVFSHandle *handle;
2824 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2826 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2827 if (info->result == GNOME_VFS_OK) {
2828 GError *error = NULL;
2829 stream = tny_vfs_stream_new (handle);
2830 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2831 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2833 if ((error->domain == TNY_ERROR_DOMAIN) &&
2834 (error->code == TNY_IO_ERROR_WRITE) &&
2835 (errno == ENOSPC)) {
2836 info->result = GNOME_VFS_ERROR_NO_SPACE;
2838 info->result = GNOME_VFS_ERROR_IO;
2841 g_object_unref (G_OBJECT (stream));
2843 g_warning ("Could not create save attachment %s: %s\n",
2844 pair->filename, gnome_vfs_result_to_string (info->result));
2847 /* Go on saving remaining files */
2848 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2849 if (info->pairs != NULL) {
2850 save_mime_part_to_file (info);
2852 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2859 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2860 SaveMimePartInfo *info)
2862 gboolean is_ok = TRUE;
2863 gint replaced_files = 0;
2864 const GList *files = info->pairs;
2865 const GList *iter, *to_replace = NULL;
2867 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2868 SaveMimePartPair *pair = iter->data;
2869 if (modest_utils_file_exists (pair->filename)) {
2871 if (replaced_files == 1)
2875 if (replaced_files) {
2878 if (replaced_files == 1) {
2879 SaveMimePartPair *pair = to_replace->data;
2880 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2881 gchar *escaped_basename, *message;
2883 escaped_basename = g_uri_unescape_string (basename, NULL);
2884 message = g_strdup_printf ("%s\n%s",
2885 _FM("docm_nc_replace_file"),
2886 (escaped_basename) ? escaped_basename : "");
2887 response = modest_platform_run_confirmation_dialog (parent, message);
2889 g_free (escaped_basename);
2891 response = modest_platform_run_confirmation_dialog (parent,
2892 _FM("docm_nc_replace_multiple"));
2894 if (response != GTK_RESPONSE_OK)
2899 save_mime_part_info_free (info, TRUE);
2901 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2907 save_attachments_response (GtkDialog *dialog,
2911 TnyList *mime_parts;
2913 GList *files_to_save = NULL;
2914 gchar *current_folder;
2916 mime_parts = TNY_LIST (user_data);
2918 if (arg1 != GTK_RESPONSE_OK)
2921 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2922 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2923 if (current_folder && current_folder != '\0') {
2925 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2926 current_folder,&err);
2928 g_debug ("Error storing latest used folder: %s", err->message);
2932 g_free (current_folder);
2934 if (!modest_utils_folder_writable (chooser_uri)) {
2935 hildon_banner_show_information
2936 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2940 iter = tny_list_create_iterator (mime_parts);
2941 while (!tny_iterator_is_done (iter)) {
2942 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2944 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2945 !tny_mime_part_is_purged (mime_part) &&
2946 (tny_mime_part_get_filename (mime_part) != NULL)) {
2947 SaveMimePartPair *pair;
2949 pair = g_slice_new0 (SaveMimePartPair);
2951 if (tny_list_get_length (mime_parts) > 1) {
2953 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2954 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2957 pair->filename = g_strdup (chooser_uri);
2959 pair->part = mime_part;
2960 files_to_save = g_list_prepend (files_to_save, pair);
2962 tny_iterator_next (iter);
2964 g_object_unref (iter);
2966 g_free (chooser_uri);
2968 if (files_to_save != NULL) {
2969 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2970 info->pairs = files_to_save;
2971 info->result = TRUE;
2972 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
2976 /* Free and close the dialog */
2977 g_object_unref (mime_parts);
2978 gtk_widget_destroy (GTK_WIDGET (dialog));
2982 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2983 TnyList *mime_parts)
2985 ModestMsgViewWindowPrivate *priv;
2986 GtkWidget *save_dialog = NULL;
2987 gchar *conf_folder = NULL;
2988 gchar *filename = NULL;
2989 gchar *save_multiple_str = NULL;
2991 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2992 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2994 if (mime_parts == NULL) {
2995 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2996 * selection available */
2997 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2998 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2999 g_object_unref (mime_parts);
3002 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3004 g_object_unref (mime_parts);
3010 g_object_ref (mime_parts);
3013 /* prepare dialog */
3014 if (tny_list_get_length (mime_parts) == 1) {
3016 /* only one attachment selected */
3017 iter = tny_list_create_iterator (mime_parts);
3018 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3019 g_object_unref (iter);
3020 if (!modest_tny_mime_part_is_msg (mime_part) &&
3021 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3022 !tny_mime_part_is_purged (mime_part)) {
3023 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3025 /* TODO: show any error? */
3026 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3027 g_object_unref (mime_parts);
3030 g_object_unref (mime_part);
3032 gint num = tny_list_get_length (mime_parts);
3033 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3034 "sfil_va_number_of_objects_attachment",
3035 "sfil_va_number_of_objects_attachments",
3039 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3040 GTK_FILE_CHOOSER_ACTION_SAVE);
3043 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3044 if (conf_folder && conf_folder[0] != '\0') {
3045 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3048 /* Set the default folder to images folder */
3049 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3050 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3051 g_free (docs_folder);
3053 g_free (conf_folder);
3057 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3062 /* if multiple, set multiple string */
3063 if (save_multiple_str) {
3064 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3065 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3068 /* We must run this asynchronously, because the hildon dialog
3069 performs a gtk_dialog_run by itself which leads to gdk
3071 g_signal_connect (save_dialog, "response",
3072 G_CALLBACK (save_attachments_response), mime_parts);
3074 gtk_widget_show_all (save_dialog);
3078 show_remove_attachment_information (gpointer userdata)
3080 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3081 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3083 /* We're outside the main lock */
3084 gdk_threads_enter ();
3086 if (priv->remove_attachment_banner != NULL) {
3087 gtk_widget_destroy (priv->remove_attachment_banner);
3088 g_object_unref (priv->remove_attachment_banner);
3091 priv->remove_attachment_banner = g_object_ref (
3092 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3094 gdk_threads_leave ();
3100 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3102 ModestMsgViewWindowPrivate *priv;
3103 TnyList *mime_parts = NULL, *tmp;
3104 gchar *confirmation_message;
3110 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3111 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3113 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3114 * because we don't have selection
3116 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3118 /* Remove already purged messages from mime parts list. We use
3119 a copy of the list to remove items in the original one */
3120 tmp = tny_list_copy (mime_parts);
3121 iter = tny_list_create_iterator (tmp);
3122 while (!tny_iterator_is_done (iter)) {
3123 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3124 if (tny_mime_part_is_purged (part))
3125 tny_list_remove (mime_parts, (GObject *) part);
3127 g_object_unref (part);
3128 tny_iterator_next (iter);
3130 g_object_unref (tmp);
3131 g_object_unref (iter);
3133 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3134 tny_list_get_length (mime_parts) == 0) {
3135 g_object_unref (mime_parts);
3139 n_attachments = tny_list_get_length (mime_parts);
3140 if (n_attachments == 1) {
3144 iter = tny_list_create_iterator (mime_parts);
3145 part = (TnyMimePart *) tny_iterator_get_current (iter);
3146 g_object_unref (iter);
3147 if (modest_tny_mime_part_is_msg (part)) {
3149 header = tny_msg_get_header (TNY_MSG (part));
3150 filename = tny_header_dup_subject (header);
3151 g_object_unref (header);
3152 if (filename == NULL)
3153 filename = g_strdup (_("mail_va_no_subject"));
3155 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3157 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3159 g_object_unref (part);
3161 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3162 "mcen_nc_purge_files_text",
3163 n_attachments), n_attachments);
3165 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3166 confirmation_message);
3167 g_free (confirmation_message);
3169 if (response != GTK_RESPONSE_OK) {
3170 g_object_unref (mime_parts);
3174 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3176 iter = tny_list_create_iterator (mime_parts);
3177 while (!tny_iterator_is_done (iter)) {
3180 part = (TnyMimePart *) tny_iterator_get_current (iter);
3181 tny_mime_part_set_purged (TNY_MIME_PART (part));
3182 g_object_unref (part);
3183 tny_iterator_next (iter);
3185 g_object_unref (iter);
3187 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3188 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3189 tny_msg_rewrite_cache (msg);
3190 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3191 g_object_unref (msg);
3192 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3194 g_object_unref (mime_parts);
3196 if (priv->purge_timeout > 0) {
3197 g_source_remove (priv->purge_timeout);
3198 priv->purge_timeout = 0;
3201 if (priv->remove_attachment_banner) {
3202 gtk_widget_destroy (priv->remove_attachment_banner);
3203 g_object_unref (priv->remove_attachment_banner);
3204 priv->remove_attachment_banner = NULL;
3210 update_window_title (ModestMsgViewWindow *window)
3212 ModestMsgViewWindowPrivate *priv;
3214 TnyHeader *header = NULL;
3215 gchar *subject = NULL;
3217 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3219 /* Note that if the window is closed while we're retrieving
3220 the message, this widget could de deleted */
3221 if (!priv->msg_view)
3224 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3226 if (priv->other_body) {
3229 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3231 g_strstrip (description);
3232 subject = description;
3234 } else if (msg != NULL) {
3235 header = tny_msg_get_header (msg);
3236 subject = tny_header_dup_subject (header);
3237 g_object_unref (header);
3238 g_object_unref (msg);
3241 if ((subject == NULL)||(subject[0] == '\0')) {
3243 subject = g_strdup (_("mail_va_no_subject"));
3246 gtk_window_set_title (GTK_WINDOW (window), subject);
3251 on_move_focus (GtkWidget *widget,
3252 GtkDirectionType direction,
3255 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3259 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3261 GnomeVFSResult result;
3262 GnomeVFSHandle *handle = NULL;
3263 GnomeVFSFileInfo *info = NULL;
3266 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3267 if (result != GNOME_VFS_OK) {
3272 info = gnome_vfs_file_info_new ();
3273 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3274 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3275 /* We put a "safe" default size for going to cache */
3276 *expected_size = (300*1024);
3278 *expected_size = info->size;
3280 gnome_vfs_file_info_unref (info);
3282 stream = tny_vfs_stream_new (handle);
3291 TnyStream *output_stream;
3292 GtkWidget *msg_view;
3297 on_fetch_image_idle_refresh_view (gpointer userdata)
3300 FetchImageData *fidata = (FetchImageData *) userdata;
3302 gdk_threads_enter ();
3303 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3304 ModestMsgViewWindowPrivate *priv;
3306 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3307 priv->fetching_images--;
3308 gtk_widget_queue_draw (fidata->msg_view);
3309 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3311 gdk_threads_leave ();
3313 g_object_unref (fidata->msg_view);
3314 g_object_unref (fidata->window);
3315 g_slice_free (FetchImageData, fidata);
3320 on_fetch_image_thread (gpointer userdata)
3322 FetchImageData *fidata = (FetchImageData *) userdata;
3323 TnyStreamCache *cache;
3324 TnyStream *cache_stream;
3326 cache = modest_runtime_get_images_cache ();
3328 tny_stream_cache_get_stream (cache,
3330 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3331 (gpointer) fidata->uri);
3332 g_free (fidata->cache_id);
3333 g_free (fidata->uri);
3335 if (cache_stream != NULL) {
3338 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3341 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3342 if (G_UNLIKELY (nb_read < 0)) {
3344 } else if (G_LIKELY (nb_read > 0)) {
3345 gssize nb_written = 0;
3347 while (G_UNLIKELY (nb_written < nb_read)) {
3350 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3351 nb_read - nb_written);
3352 if (G_UNLIKELY (len < 0))
3358 tny_stream_close (cache_stream);
3359 g_object_unref (cache_stream);
3362 tny_stream_close (fidata->output_stream);
3363 g_object_unref (fidata->output_stream);
3365 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3371 on_fetch_image (ModestMsgView *msgview,
3374 ModestMsgViewWindow *window)
3376 const gchar *current_account;
3377 ModestMsgViewWindowPrivate *priv;
3378 FetchImageData *fidata;
3380 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3382 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3384 fidata = g_slice_new0 (FetchImageData);
3385 fidata->msg_view = g_object_ref (msgview);
3386 fidata->window = g_object_ref (window);
3387 fidata->uri = g_strdup (uri);
3388 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3389 fidata->output_stream = g_object_ref (stream);
3391 priv->fetching_images++;
3392 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3393 g_object_unref (fidata->output_stream);
3394 g_free (fidata->cache_id);
3395 g_free (fidata->uri);
3396 g_object_unref (fidata->msg_view);
3397 g_slice_free (FetchImageData, fidata);
3398 tny_stream_close (stream);
3399 priv->fetching_images--;
3400 update_progress_hint (window);
3403 update_progress_hint (window);
3409 setup_menu (ModestMsgViewWindow *self)
3411 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3413 /* Settings menu buttons */
3414 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3415 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3416 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3418 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_move_message"), NULL,
3419 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3420 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3422 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3423 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3424 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3426 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3427 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3428 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3430 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3431 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3432 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3434 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3435 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3436 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3437 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3438 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3439 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3441 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3442 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3443 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3444 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3445 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3446 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3448 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3449 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3450 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3454 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3456 ModestMsgViewWindowPrivate *priv;
3457 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3458 GSList *recipients = NULL;
3460 gboolean contacts_to_add = FALSE;
3462 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3466 header = modest_msg_view_window_get_header (self);
3469 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3470 g_object_unref (header);
3472 recipients = modest_tny_msg_get_all_recipients_list (msg);
3473 g_object_unref (msg);
3476 if (recipients != NULL) {
3477 GtkWidget *picker_dialog;
3478 GtkWidget *selector;
3480 gchar *selected = NULL;
3482 selector = hildon_touch_selector_new_text ();
3483 g_object_ref (selector);
3485 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3486 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3487 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3488 (const gchar *) node->data);
3489 contacts_to_add = TRUE;
3493 if (contacts_to_add) {
3496 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3497 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3499 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3500 HILDON_TOUCH_SELECTOR (selector));
3502 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3504 if (picker_result == GTK_RESPONSE_OK) {
3505 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3507 gtk_widget_destroy (picker_dialog);
3510 modest_address_book_add_address (selected);
3515 g_object_unref (selector);
3520 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3524 _modest_msg_view_window_map_event (GtkWidget *widget,
3528 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3530 update_progress_hint (self);
3536 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3538 ModestMsgViewWindowPrivate *priv;
3539 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3541 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3545 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3547 ModestMsgViewWindowPrivate *priv;
3548 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3550 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3552 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3556 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3558 ModestMsgViewWindowPrivate *priv;
3561 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3563 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3564 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3566 if (!message_reader (self, priv, header, NULL, NULL, priv->row_reference)) {
3567 g_warning ("Shouldn't happen, trying to reload a message failed");
3570 g_object_unref (header);
3574 update_branding (ModestMsgViewWindow *self)
3576 const gchar *account;
3577 const gchar *mailbox;
3578 ModestAccountMgr *mgr;
3579 ModestProtocol *protocol = NULL;
3580 gchar *service_name = NULL;
3581 const GdkPixbuf *service_icon = NULL;
3582 ModestMsgViewWindowPrivate *priv;
3584 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3586 account = modest_window_get_active_account (MODEST_WINDOW (self));
3587 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3589 mgr = modest_runtime_get_account_mgr ();
3591 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3592 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3593 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3595 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3596 account, mailbox, MODEST_ICON_SIZE_SMALL);
3600 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3601 g_free (service_name);