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;
2775 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2776 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2777 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2778 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2781 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2785 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2786 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2787 g_free (pair->filename);
2788 g_object_unref (pair->part);
2789 g_slice_free (SaveMimePartPair, pair);
2791 g_list_free (info->pairs);
2795 g_slice_free (SaveMimePartInfo, info);
2800 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2802 /* This is a GDK lock because we are an idle callback and
2803 * hildon_banner_show_information is or does Gtk+ code */
2805 gdk_threads_enter (); /* CHECKED */
2806 if (info->result == GNOME_VFS_OK) {
2807 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2808 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2811 /* Check if the uri belongs to the external mmc */
2812 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2813 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2815 msg = g_strdup (_KR("cerm_memory_card_full"));
2816 modest_platform_information_banner (NULL, NULL, msg);
2819 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2821 save_mime_part_info_free (info, FALSE);
2822 gdk_threads_leave (); /* CHECKED */
2828 save_mime_part_to_file (SaveMimePartInfo *info)
2830 GnomeVFSHandle *handle;
2832 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2834 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2835 if (info->result == GNOME_VFS_OK) {
2836 GError *error = NULL;
2837 stream = tny_vfs_stream_new (handle);
2838 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2839 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2841 if ((error->domain == TNY_ERROR_DOMAIN) &&
2842 (error->code == TNY_IO_ERROR_WRITE) &&
2843 (errno == ENOSPC)) {
2844 info->result = GNOME_VFS_ERROR_NO_SPACE;
2846 info->result = GNOME_VFS_ERROR_IO;
2849 g_object_unref (G_OBJECT (stream));
2851 g_warning ("Could not create save attachment %s: %s\n",
2852 pair->filename, gnome_vfs_result_to_string (info->result));
2855 /* Go on saving remaining files */
2856 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2857 if (info->pairs != NULL) {
2858 save_mime_part_to_file (info);
2860 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2867 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2868 SaveMimePartInfo *info)
2870 gboolean is_ok = TRUE;
2871 gint replaced_files = 0;
2872 const GList *files = info->pairs;
2873 const GList *iter, *to_replace = NULL;
2875 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2876 SaveMimePartPair *pair = iter->data;
2877 if (modest_utils_file_exists (pair->filename)) {
2879 if (replaced_files == 1)
2883 if (replaced_files) {
2886 if (replaced_files == 1) {
2887 SaveMimePartPair *pair = to_replace->data;
2888 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2889 gchar *escaped_basename, *message;
2891 escaped_basename = g_uri_unescape_string (basename, NULL);
2892 message = g_strdup_printf ("%s\n%s",
2893 _FM("docm_nc_replace_file"),
2894 (escaped_basename) ? escaped_basename : "");
2895 response = modest_platform_run_confirmation_dialog (parent, message);
2897 g_free (escaped_basename);
2899 response = modest_platform_run_confirmation_dialog (parent,
2900 _FM("docm_nc_replace_multiple"));
2902 if (response != GTK_RESPONSE_OK)
2907 save_mime_part_info_free (info, TRUE);
2909 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2915 save_attachments_response (GtkDialog *dialog,
2919 TnyList *mime_parts;
2921 GList *files_to_save = NULL;
2922 gchar *current_folder;
2924 mime_parts = TNY_LIST (user_data);
2926 if (arg1 != GTK_RESPONSE_OK)
2929 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2930 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2931 if (current_folder && current_folder != '\0') {
2933 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2934 current_folder,&err);
2936 g_debug ("Error storing latest used folder: %s", err->message);
2940 g_free (current_folder);
2942 if (!modest_utils_folder_writable (chooser_uri)) {
2943 hildon_banner_show_information
2944 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2948 iter = tny_list_create_iterator (mime_parts);
2949 while (!tny_iterator_is_done (iter)) {
2950 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2952 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2953 !tny_mime_part_is_purged (mime_part) &&
2954 (tny_mime_part_get_filename (mime_part) != NULL)) {
2955 SaveMimePartPair *pair;
2957 pair = g_slice_new0 (SaveMimePartPair);
2959 if (tny_list_get_length (mime_parts) > 1) {
2961 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2962 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2965 pair->filename = g_strdup (chooser_uri);
2967 pair->part = mime_part;
2968 files_to_save = g_list_prepend (files_to_save, pair);
2970 tny_iterator_next (iter);
2972 g_object_unref (iter);
2975 if (files_to_save != NULL) {
2976 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2977 info->pairs = files_to_save;
2978 info->result = TRUE;
2979 info->uri = g_strdup (chooser_uri);
2980 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
2982 g_free (chooser_uri);
2985 /* Free and close the dialog */
2986 g_object_unref (mime_parts);
2987 gtk_widget_destroy (GTK_WIDGET (dialog));
2991 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2992 TnyList *mime_parts)
2994 ModestMsgViewWindowPrivate *priv;
2995 GtkWidget *save_dialog = NULL;
2996 gchar *conf_folder = NULL;
2997 gchar *filename = NULL;
2998 gchar *save_multiple_str = NULL;
3000 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3001 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3003 if (mime_parts == NULL) {
3004 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3005 * selection available */
3006 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3007 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3008 g_object_unref (mime_parts);
3011 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3013 g_object_unref (mime_parts);
3019 g_object_ref (mime_parts);
3022 /* prepare dialog */
3023 if (tny_list_get_length (mime_parts) == 1) {
3025 /* only one attachment selected */
3026 iter = tny_list_create_iterator (mime_parts);
3027 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3028 g_object_unref (iter);
3029 if (!modest_tny_mime_part_is_msg (mime_part) &&
3030 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3031 !tny_mime_part_is_purged (mime_part)) {
3032 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3034 /* TODO: show any error? */
3035 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3036 g_object_unref (mime_parts);
3039 g_object_unref (mime_part);
3041 gint num = tny_list_get_length (mime_parts);
3042 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3043 "sfil_va_number_of_objects_attachment",
3044 "sfil_va_number_of_objects_attachments",
3048 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3049 GTK_FILE_CHOOSER_ACTION_SAVE);
3052 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3053 if (conf_folder && conf_folder[0] != '\0') {
3054 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3057 /* Set the default folder to images folder */
3058 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3059 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3060 g_free (docs_folder);
3062 g_free (conf_folder);
3066 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3071 /* if multiple, set multiple string */
3072 if (save_multiple_str) {
3073 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3074 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3077 /* We must run this asynchronously, because the hildon dialog
3078 performs a gtk_dialog_run by itself which leads to gdk
3080 g_signal_connect (save_dialog, "response",
3081 G_CALLBACK (save_attachments_response), mime_parts);
3083 gtk_widget_show_all (save_dialog);
3087 show_remove_attachment_information (gpointer userdata)
3089 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3090 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3092 /* We're outside the main lock */
3093 gdk_threads_enter ();
3095 if (priv->remove_attachment_banner != NULL) {
3096 gtk_widget_destroy (priv->remove_attachment_banner);
3097 g_object_unref (priv->remove_attachment_banner);
3100 priv->remove_attachment_banner = g_object_ref (
3101 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3103 gdk_threads_leave ();
3109 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3111 ModestMsgViewWindowPrivate *priv;
3112 TnyList *mime_parts = NULL, *tmp;
3113 gchar *confirmation_message;
3119 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3120 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3122 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3123 * because we don't have selection
3125 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3127 /* Remove already purged messages from mime parts list. We use
3128 a copy of the list to remove items in the original one */
3129 tmp = tny_list_copy (mime_parts);
3130 iter = tny_list_create_iterator (tmp);
3131 while (!tny_iterator_is_done (iter)) {
3132 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3133 if (tny_mime_part_is_purged (part))
3134 tny_list_remove (mime_parts, (GObject *) part);
3136 g_object_unref (part);
3137 tny_iterator_next (iter);
3139 g_object_unref (tmp);
3140 g_object_unref (iter);
3142 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3143 tny_list_get_length (mime_parts) == 0) {
3144 g_object_unref (mime_parts);
3148 n_attachments = tny_list_get_length (mime_parts);
3149 if (n_attachments == 1) {
3153 iter = tny_list_create_iterator (mime_parts);
3154 part = (TnyMimePart *) tny_iterator_get_current (iter);
3155 g_object_unref (iter);
3156 if (modest_tny_mime_part_is_msg (part)) {
3158 header = tny_msg_get_header (TNY_MSG (part));
3159 filename = tny_header_dup_subject (header);
3160 g_object_unref (header);
3161 if (filename == NULL)
3162 filename = g_strdup (_("mail_va_no_subject"));
3164 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3166 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3168 g_object_unref (part);
3170 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3171 "mcen_nc_purge_files_text",
3172 n_attachments), n_attachments);
3174 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3175 confirmation_message);
3176 g_free (confirmation_message);
3178 if (response != GTK_RESPONSE_OK) {
3179 g_object_unref (mime_parts);
3183 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3185 iter = tny_list_create_iterator (mime_parts);
3186 while (!tny_iterator_is_done (iter)) {
3189 part = (TnyMimePart *) tny_iterator_get_current (iter);
3190 tny_mime_part_set_purged (TNY_MIME_PART (part));
3191 g_object_unref (part);
3192 tny_iterator_next (iter);
3194 g_object_unref (iter);
3196 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3197 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3198 tny_msg_rewrite_cache (msg);
3199 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3200 g_object_unref (msg);
3201 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3203 g_object_unref (mime_parts);
3205 if (priv->purge_timeout > 0) {
3206 g_source_remove (priv->purge_timeout);
3207 priv->purge_timeout = 0;
3210 if (priv->remove_attachment_banner) {
3211 gtk_widget_destroy (priv->remove_attachment_banner);
3212 g_object_unref (priv->remove_attachment_banner);
3213 priv->remove_attachment_banner = NULL;
3219 update_window_title (ModestMsgViewWindow *window)
3221 ModestMsgViewWindowPrivate *priv;
3223 TnyHeader *header = NULL;
3224 gchar *subject = NULL;
3226 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3228 /* Note that if the window is closed while we're retrieving
3229 the message, this widget could de deleted */
3230 if (!priv->msg_view)
3233 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3235 if (priv->other_body) {
3238 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3240 g_strstrip (description);
3241 subject = description;
3243 } else if (msg != NULL) {
3244 header = tny_msg_get_header (msg);
3245 subject = tny_header_dup_subject (header);
3246 g_object_unref (header);
3247 g_object_unref (msg);
3250 if ((subject == NULL)||(subject[0] == '\0')) {
3252 subject = g_strdup (_("mail_va_no_subject"));
3255 gtk_window_set_title (GTK_WINDOW (window), subject);
3260 on_move_focus (GtkWidget *widget,
3261 GtkDirectionType direction,
3264 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3268 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3270 GnomeVFSResult result;
3271 GnomeVFSHandle *handle = NULL;
3272 GnomeVFSFileInfo *info = NULL;
3275 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3276 if (result != GNOME_VFS_OK) {
3281 info = gnome_vfs_file_info_new ();
3282 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3283 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3284 /* We put a "safe" default size for going to cache */
3285 *expected_size = (300*1024);
3287 *expected_size = info->size;
3289 gnome_vfs_file_info_unref (info);
3291 stream = tny_vfs_stream_new (handle);
3300 TnyStream *output_stream;
3301 GtkWidget *msg_view;
3306 on_fetch_image_idle_refresh_view (gpointer userdata)
3309 FetchImageData *fidata = (FetchImageData *) userdata;
3311 gdk_threads_enter ();
3312 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3313 ModestMsgViewWindowPrivate *priv;
3315 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3316 priv->fetching_images--;
3317 gtk_widget_queue_draw (fidata->msg_view);
3318 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3320 gdk_threads_leave ();
3322 g_object_unref (fidata->msg_view);
3323 g_object_unref (fidata->window);
3324 g_slice_free (FetchImageData, fidata);
3329 on_fetch_image_thread (gpointer userdata)
3331 FetchImageData *fidata = (FetchImageData *) userdata;
3332 TnyStreamCache *cache;
3333 TnyStream *cache_stream;
3335 cache = modest_runtime_get_images_cache ();
3337 tny_stream_cache_get_stream (cache,
3339 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3340 (gpointer) fidata->uri);
3341 g_free (fidata->cache_id);
3342 g_free (fidata->uri);
3344 if (cache_stream != NULL) {
3347 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3350 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3351 if (G_UNLIKELY (nb_read < 0)) {
3353 } else if (G_LIKELY (nb_read > 0)) {
3354 gssize nb_written = 0;
3356 while (G_UNLIKELY (nb_written < nb_read)) {
3359 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3360 nb_read - nb_written);
3361 if (G_UNLIKELY (len < 0))
3367 tny_stream_close (cache_stream);
3368 g_object_unref (cache_stream);
3371 tny_stream_close (fidata->output_stream);
3372 g_object_unref (fidata->output_stream);
3374 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3380 on_fetch_image (ModestMsgView *msgview,
3383 ModestMsgViewWindow *window)
3385 const gchar *current_account;
3386 ModestMsgViewWindowPrivate *priv;
3387 FetchImageData *fidata;
3389 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3391 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3393 fidata = g_slice_new0 (FetchImageData);
3394 fidata->msg_view = g_object_ref (msgview);
3395 fidata->window = g_object_ref (window);
3396 fidata->uri = g_strdup (uri);
3397 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3398 fidata->output_stream = g_object_ref (stream);
3400 priv->fetching_images++;
3401 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3402 g_object_unref (fidata->output_stream);
3403 g_free (fidata->cache_id);
3404 g_free (fidata->uri);
3405 g_object_unref (fidata->msg_view);
3406 g_slice_free (FetchImageData, fidata);
3407 tny_stream_close (stream);
3408 priv->fetching_images--;
3409 update_progress_hint (window);
3412 update_progress_hint (window);
3418 setup_menu (ModestMsgViewWindow *self)
3420 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3422 /* Settings menu buttons */
3423 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3424 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3425 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3427 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3428 dngettext(GETTEXT_PACKAGE,
3429 "mcen_me_move_message",
3430 "mcen_me_move_messages",
3433 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3434 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3436 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3437 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3438 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3440 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3441 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3442 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3444 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3445 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3446 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3448 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3449 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3450 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3451 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3452 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3453 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3455 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3456 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3457 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3458 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3459 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3460 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3462 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3463 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3464 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3468 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3470 ModestMsgViewWindowPrivate *priv;
3471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3472 GSList *recipients = NULL;
3474 gboolean contacts_to_add = FALSE;
3476 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3480 header = modest_msg_view_window_get_header (self);
3483 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3484 g_object_unref (header);
3486 recipients = modest_tny_msg_get_all_recipients_list (msg);
3487 g_object_unref (msg);
3490 if (recipients != NULL) {
3491 GtkWidget *picker_dialog;
3492 GtkWidget *selector;
3494 gchar *selected = NULL;
3496 selector = hildon_touch_selector_new_text ();
3497 g_object_ref (selector);
3499 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3500 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3501 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3502 (const gchar *) node->data);
3503 contacts_to_add = TRUE;
3507 if (contacts_to_add) {
3510 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3511 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3513 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3514 HILDON_TOUCH_SELECTOR (selector));
3516 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3518 if (picker_result == GTK_RESPONSE_OK) {
3519 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3521 gtk_widget_destroy (picker_dialog);
3524 modest_address_book_add_address (selected, (GtkWindow *) self);
3529 g_object_unref (selector);
3534 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3538 _modest_msg_view_window_map_event (GtkWidget *widget,
3542 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3544 update_progress_hint (self);
3550 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3552 ModestMsgViewWindowPrivate *priv;
3553 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3555 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3559 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3561 ModestMsgViewWindowPrivate *priv;
3562 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3564 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3566 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3570 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3572 ModestMsgViewWindowPrivate *priv;
3575 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3577 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3578 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3580 if (!message_reader (self, priv, header, NULL, NULL, priv->row_reference)) {
3581 g_warning ("Shouldn't happen, trying to reload a message failed");
3584 g_object_unref (header);
3588 update_branding (ModestMsgViewWindow *self)
3590 const gchar *account;
3591 const gchar *mailbox;
3592 ModestAccountMgr *mgr;
3593 ModestProtocol *protocol = NULL;
3594 gchar *service_name = NULL;
3595 const GdkPixbuf *service_icon = NULL;
3596 ModestMsgViewWindowPrivate *priv;
3598 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3600 account = modest_window_get_active_account (MODEST_WINDOW (self));
3601 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3603 mgr = modest_runtime_get_account_mgr ();
3605 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3606 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3607 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3609 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3610 account, mailbox, MODEST_ICON_SIZE_SMALL);
3614 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3615 g_free (service_name);