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>
70 #define MYDOCS_ENV "MYDOCSDIR"
71 #define DOCS_FOLDER ".documents"
73 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
74 struct _ModestMsgViewWindowPrivate {
77 GtkWidget *main_scroll;
78 GtkWidget *find_toolbar;
81 /* Progress observers */
82 GSList *progress_widgets;
85 GtkWidget *prev_toolitem;
86 GtkWidget *next_toolitem;
87 gboolean progress_hint;
90 /* Optimized view enabled */
91 gboolean optimized_view;
93 /* Whether this was created via the *_new_for_search_result() function. */
94 gboolean is_search_result;
96 /* Whether the message is in outbox */
99 /* A reference to the @model of the header view
100 * to allow selecting previous/next messages,
101 * if the message is currently selected in the header view.
103 const gchar *header_folder_id;
104 GtkTreeModel *header_model;
105 GtkTreeRowReference *row_reference;
106 GtkTreeRowReference *next_row_reference;
108 gulong clipboard_change_handler;
109 gulong queue_change_handler;
110 gulong account_removed_handler;
111 gulong row_changed_handler;
112 gulong row_deleted_handler;
113 gulong row_inserted_handler;
114 gulong rows_reordered_handler;
117 GtkWidget *remove_attachment_banner;
120 TnyMimePart *other_body;
125 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
126 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
127 static void modest_header_view_observer_init(
128 ModestHeaderViewObserverIface *iface_class);
129 static void modest_msg_view_window_finalize (GObject *obj);
130 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
132 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
133 ModestMsgViewWindow *obj);
134 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
135 ModestMsgViewWindow *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 gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
206 static void init_window (ModestMsgViewWindow *obj);
208 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
210 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
212 static gboolean on_fetch_image (ModestMsgView *msgview,
215 ModestMsgViewWindow *window);
217 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
218 GtkScrollType scroll_type,
221 static gboolean message_reader (ModestMsgViewWindow *window,
222 ModestMsgViewWindowPrivate *priv,
224 GtkTreeRowReference *row_reference);
226 static void setup_menu (ModestMsgViewWindow *self);
227 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
232 /* list my signals */
239 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
240 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
243 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
244 MODEST_TYPE_MSG_VIEW_WINDOW, \
245 ModestMsgViewWindowPrivate))
247 static GtkWindowClass *parent_class = NULL;
249 /* uncomment the following if you have defined any signals */
250 static guint signals[LAST_SIGNAL] = {0};
253 modest_msg_view_window_get_type (void)
255 static GType my_type = 0;
257 static const GTypeInfo my_info = {
258 sizeof(ModestMsgViewWindowClass),
259 NULL, /* base init */
260 NULL, /* base finalize */
261 (GClassInitFunc) modest_msg_view_window_class_init,
262 NULL, /* class finalize */
263 NULL, /* class data */
264 sizeof(ModestMsgViewWindow),
266 (GInstanceInitFunc) modest_msg_view_window_init,
269 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
270 "ModestMsgViewWindow",
273 static const GInterfaceInfo modest_header_view_observer_info =
275 (GInterfaceInitFunc) modest_header_view_observer_init,
276 NULL, /* interface_finalize */
277 NULL /* interface_data */
280 g_type_add_interface_static (my_type,
281 MODEST_TYPE_HEADER_VIEW_OBSERVER,
282 &modest_header_view_observer_info);
288 save_state (ModestWindow *self)
290 modest_widget_memory_save (modest_runtime_get_conf (),
292 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
296 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
297 GtkScrollType scroll_type,
301 ModestMsgViewWindowPrivate *priv;
302 gboolean return_value;
304 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
305 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
310 add_scroll_binding (GtkBindingSet *binding_set,
312 GtkScrollType scroll)
314 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
316 gtk_binding_entry_add_signal (binding_set, keyval, 0,
318 GTK_TYPE_SCROLL_TYPE, scroll,
319 G_TYPE_BOOLEAN, FALSE);
320 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
322 GTK_TYPE_SCROLL_TYPE, scroll,
323 G_TYPE_BOOLEAN, FALSE);
327 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
329 GObjectClass *gobject_class;
330 HildonWindowClass *hildon_window_class;
331 ModestWindowClass *modest_window_class;
332 GtkBindingSet *binding_set;
334 gobject_class = (GObjectClass*) klass;
335 hildon_window_class = (HildonWindowClass *) klass;
336 modest_window_class = (ModestWindowClass *) klass;
338 parent_class = g_type_class_peek_parent (klass);
339 gobject_class->finalize = modest_msg_view_window_finalize;
341 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
342 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
343 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
344 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
345 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
346 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
348 modest_window_class->save_state_func = save_state;
350 klass->scroll_child = modest_msg_view_window_scroll_child;
352 signals[MSG_CHANGED_SIGNAL] =
353 g_signal_new ("msg-changed",
354 G_TYPE_FROM_CLASS (gobject_class),
356 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
358 modest_marshal_VOID__POINTER_POINTER,
359 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
361 signals[SCROLL_CHILD_SIGNAL] =
362 g_signal_new ("scroll-child",
363 G_TYPE_FROM_CLASS (gobject_class),
364 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
365 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
367 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
368 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
370 binding_set = gtk_binding_set_by_class (klass);
371 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
372 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
373 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
374 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
375 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
376 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
378 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
382 static void modest_header_view_observer_init(
383 ModestHeaderViewObserverIface *iface_class)
385 iface_class->update_func = modest_msg_view_window_update_model_replaced;
389 modest_msg_view_window_init (ModestMsgViewWindow *obj)
391 ModestMsgViewWindowPrivate *priv;
392 ModestWindowPrivate *parent_priv = NULL;
393 GtkActionGroup *action_group = NULL;
394 GError *error = NULL;
395 GdkPixbuf *window_icon;
397 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
398 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
399 parent_priv->ui_manager = gtk_ui_manager_new();
401 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
402 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
404 /* Add common actions */
405 gtk_action_group_add_actions (action_group,
406 modest_action_entries,
407 G_N_ELEMENTS (modest_action_entries),
409 gtk_action_group_add_toggle_actions (action_group,
410 msg_view_toggle_action_entries,
411 G_N_ELEMENTS (msg_view_toggle_action_entries),
414 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
415 g_object_unref (action_group);
417 /* Load the UI definition */
418 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
421 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
422 g_error_free (error);
427 /* Add accelerators */
428 gtk_window_add_accel_group (GTK_WINDOW (obj),
429 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
431 priv->is_search_result = FALSE;
432 priv->is_outbox = FALSE;
434 priv->msg_view = NULL;
435 priv->header_model = NULL;
436 priv->header_folder_id = NULL;
437 priv->clipboard_change_handler = 0;
438 priv->queue_change_handler = 0;
439 priv->account_removed_handler = 0;
440 priv->row_changed_handler = 0;
441 priv->row_deleted_handler = 0;
442 priv->row_inserted_handler = 0;
443 priv->rows_reordered_handler = 0;
444 priv->progress_hint = FALSE;
445 priv->fetching_images = 0;
447 priv->optimized_view = FALSE;
448 priv->purge_timeout = 0;
449 priv->remove_attachment_banner = NULL;
450 priv->msg_uid = NULL;
451 priv->other_body = NULL;
453 priv->sighandlers = NULL;
456 init_window (MODEST_MSG_VIEW_WINDOW(obj));
458 /* Set window icon */
459 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
461 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
462 g_object_unref (window_icon);
465 hildon_program_add_window (hildon_program_get_instance(),
472 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
474 ModestMsgViewWindowPrivate *priv = NULL;
476 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
478 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
480 set_progress_hint (self, TRUE);
486 update_progress_hint (ModestMsgViewWindow *self)
488 ModestMsgViewWindowPrivate *priv;
489 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
491 if (GTK_WIDGET_VISIBLE (self)) {
492 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
493 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
498 set_progress_hint (ModestMsgViewWindow *self,
501 ModestWindowPrivate *parent_priv;
502 ModestMsgViewWindowPrivate *priv;
504 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
506 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
507 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
509 /* Sets current progress hint */
510 priv->progress_hint = enabled;
512 update_progress_hint (self);
518 init_window (ModestMsgViewWindow *obj)
520 GtkWidget *main_vbox;
521 ModestMsgViewWindowPrivate *priv;
523 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
525 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
526 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
527 main_vbox = gtk_vbox_new (FALSE, 6);
528 priv->main_scroll = hildon_pannable_area_new ();
529 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
530 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
531 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
533 priv->find_toolbar = hildon_find_toolbar_new (NULL);
534 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
535 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
537 /* NULL-ize fields if the window is destroyed */
538 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
540 gtk_widget_show_all (GTK_WIDGET(main_vbox));
544 modest_msg_view_window_disconnect_signals (ModestWindow *self)
546 ModestMsgViewWindowPrivate *priv;
547 GtkWidget *header_view = NULL;
548 GtkWindow *parent_window = NULL;
550 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
552 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
553 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
554 priv->clipboard_change_handler))
555 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
556 priv->clipboard_change_handler);
558 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
559 priv->queue_change_handler))
560 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
561 priv->queue_change_handler);
563 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
564 priv->account_removed_handler))
565 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
566 priv->account_removed_handler);
568 if (priv->header_model) {
569 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
570 priv->row_changed_handler))
571 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
572 priv->row_changed_handler);
574 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
575 priv->row_deleted_handler))
576 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
577 priv->row_deleted_handler);
579 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
580 priv->row_inserted_handler))
581 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
582 priv->row_inserted_handler);
584 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
585 priv->rows_reordered_handler))
586 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
587 priv->rows_reordered_handler);
590 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
591 priv->sighandlers = NULL;
593 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
594 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
595 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
597 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
598 MODEST_HEADER_VIEW_OBSERVER(self));
604 modest_msg_view_window_finalize (GObject *obj)
606 ModestMsgViewWindowPrivate *priv;
608 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
610 /* Sanity check: shouldn't be needed, the window mgr should
611 call this function before */
612 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
614 if (priv->other_body != NULL) {
615 g_object_unref (priv->other_body);
616 priv->other_body = NULL;
619 if (priv->header_model != NULL) {
620 g_object_unref (priv->header_model);
621 priv->header_model = NULL;
624 if (priv->remove_attachment_banner) {
625 gtk_widget_destroy (priv->remove_attachment_banner);
626 g_object_unref (priv->remove_attachment_banner);
627 priv->remove_attachment_banner = NULL;
630 if (priv->purge_timeout > 0) {
631 g_source_remove (priv->purge_timeout);
632 priv->purge_timeout = 0;
635 if (priv->row_reference) {
636 gtk_tree_row_reference_free (priv->row_reference);
637 priv->row_reference = NULL;
640 if (priv->next_row_reference) {
641 gtk_tree_row_reference_free (priv->next_row_reference);
642 priv->next_row_reference = NULL;
646 g_free (priv->msg_uid);
647 priv->msg_uid = NULL;
650 G_OBJECT_CLASS(parent_class)->finalize (obj);
654 select_next_valid_row (GtkTreeModel *model,
655 GtkTreeRowReference **row_reference,
659 GtkTreeIter tmp_iter;
661 GtkTreePath *next = NULL;
662 gboolean retval = FALSE, finished;
664 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
666 path = gtk_tree_row_reference_get_path (*row_reference);
667 gtk_tree_model_get_iter (model, &tmp_iter, path);
668 gtk_tree_row_reference_free (*row_reference);
669 *row_reference = NULL;
673 TnyHeader *header = NULL;
675 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
676 gtk_tree_model_get (model, &tmp_iter,
677 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
681 if (msg_is_visible (header, is_outbox)) {
682 next = gtk_tree_model_get_path (model, &tmp_iter);
683 *row_reference = gtk_tree_row_reference_new (model, next);
684 gtk_tree_path_free (next);
688 g_object_unref (header);
691 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
692 next = gtk_tree_model_get_path (model, &tmp_iter);
694 /* Ensure that we are not selecting the same */
695 if (gtk_tree_path_compare (path, next) != 0) {
696 gtk_tree_model_get (model, &tmp_iter,
697 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
700 if (msg_is_visible (header, is_outbox)) {
701 *row_reference = gtk_tree_row_reference_new (model, next);
705 g_object_unref (header);
709 /* If we ended up in the same message
710 then there is no valid next
714 gtk_tree_path_free (next);
716 /* If there are no more messages and we don't
717 want to start again in the first one then
718 there is no valid next message */
724 gtk_tree_path_free (path);
729 /* TODO: This should be in _init(), with the parameters as properties. */
731 modest_msg_view_window_construct (ModestMsgViewWindow *self,
732 const gchar *modest_account_name,
733 const gchar *mailbox,
734 const gchar *msg_uid)
737 ModestMsgViewWindowPrivate *priv = NULL;
738 ModestWindowPrivate *parent_priv = NULL;
739 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
740 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
742 obj = G_OBJECT (self);
743 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
744 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
746 priv->msg_uid = g_strdup (msg_uid);
749 parent_priv->menubar = NULL;
751 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
752 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
755 /* Add common dimming rules */
756 modest_dimming_rules_group_add_rules (toolbar_rules_group,
757 modest_msg_view_toolbar_dimming_entries,
758 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
759 MODEST_WINDOW (self));
760 modest_dimming_rules_group_add_rules (clipboard_rules_group,
761 modest_msg_view_clipboard_dimming_entries,
762 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
763 MODEST_WINDOW (self));
765 /* Insert dimming rules group for this window */
766 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
767 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
768 g_object_unref (toolbar_rules_group);
769 g_object_unref (clipboard_rules_group);
771 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
773 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);
774 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
775 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
776 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
777 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
778 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
779 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
780 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
781 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
782 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
783 G_CALLBACK (modest_ui_actions_on_details), obj);
784 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
785 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
786 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
787 G_CALLBACK (on_fetch_image), obj);
789 g_signal_connect (G_OBJECT (obj), "key-release-event",
790 G_CALLBACK (modest_msg_view_window_key_event),
793 g_signal_connect (G_OBJECT (obj), "key-press-event",
794 G_CALLBACK (modest_msg_view_window_key_event),
797 g_signal_connect (G_OBJECT (obj), "move-focus",
798 G_CALLBACK (on_move_focus), obj);
800 g_signal_connect (G_OBJECT (obj), "map-event",
801 G_CALLBACK (_modest_msg_view_window_map_event),
804 /* Mail Operation Queue */
805 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
807 G_CALLBACK (on_queue_changed),
810 /* Account manager */
811 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
813 G_CALLBACK(on_account_removed),
816 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
817 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
819 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
820 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
821 priv->last_search = NULL;
823 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
825 /* Init the clipboard actions dim status */
826 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
828 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
833 /* FIXME: parameter checks */
835 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
836 const gchar *modest_account_name,
837 const gchar *mailbox,
838 const gchar *msg_uid,
840 GtkTreeRowReference *row_reference)
842 ModestMsgViewWindow *window = NULL;
843 ModestMsgViewWindowPrivate *priv = NULL;
844 TnyFolder *header_folder = NULL;
845 ModestHeaderView *header_view = NULL;
846 ModestWindowMgr *mgr = NULL;
849 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
852 mgr = modest_runtime_get_window_mgr ();
853 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
854 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
856 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
858 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
860 /* Remember the message list's TreeModel so we can detect changes
861 * and change the list selection when necessary: */
862 header_folder = modest_header_view_get_folder (header_view);
864 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
865 TNY_FOLDER_TYPE_OUTBOX);
866 priv->header_folder_id = tny_folder_get_id (header_folder);
867 g_object_unref(header_folder);
870 /* Setup row references and connect signals */
871 priv->header_model = g_object_ref (model);
874 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
875 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
876 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
878 priv->row_reference = NULL;
879 priv->next_row_reference = NULL;
882 /* Connect signals */
883 priv->row_changed_handler =
884 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
885 G_CALLBACK(modest_msg_view_window_on_row_changed),
887 priv->row_deleted_handler =
888 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
889 G_CALLBACK(modest_msg_view_window_on_row_deleted),
891 priv->row_inserted_handler =
892 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
893 G_CALLBACK(modest_msg_view_window_on_row_inserted),
895 priv->rows_reordered_handler =
896 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
897 G_CALLBACK(modest_msg_view_window_on_row_reordered),
900 if (header_view != NULL){
901 modest_header_view_add_observer(header_view,
902 MODEST_HEADER_VIEW_OBSERVER(window));
905 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
906 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
908 /* gtk_widget_show_all (GTK_WIDGET (window)); */
909 modest_msg_view_window_update_priority (window);
910 /* Check dimming rules */
911 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
912 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
913 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
915 return MODEST_WINDOW(window);
919 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
920 const gchar *modest_account_name,
921 const gchar *mailbox,
922 const gchar *msg_uid,
923 GtkTreeRowReference *row_reference)
925 ModestMsgViewWindow *window = NULL;
926 ModestMsgViewWindowPrivate *priv = NULL;
927 TnyFolder *header_folder = NULL;
928 ModestWindowMgr *mgr = NULL;
932 mgr = modest_runtime_get_window_mgr ();
933 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
934 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
936 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
938 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
940 /* Remember the message list's TreeModel so we can detect changes
941 * and change the list selection when necessary: */
943 if (header_view != NULL){
944 header_folder = modest_header_view_get_folder(header_view);
945 /* This could happen if the header folder was
946 unseleted before opening this msg window (for
947 example if the user selects an account in the
948 folder view of the main window */
950 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
951 TNY_FOLDER_TYPE_OUTBOX);
952 priv->header_folder_id = tny_folder_get_id(header_folder);
953 g_object_unref(header_folder);
957 /* Setup row references and connect signals */
958 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
959 g_object_ref (priv->header_model);
962 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
963 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
964 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
966 priv->row_reference = NULL;
967 priv->next_row_reference = NULL;
970 /* Connect signals */
971 priv->row_changed_handler =
972 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
973 G_CALLBACK(modest_msg_view_window_on_row_changed),
975 priv->row_deleted_handler =
976 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
977 G_CALLBACK(modest_msg_view_window_on_row_deleted),
979 priv->row_inserted_handler =
980 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
981 G_CALLBACK(modest_msg_view_window_on_row_inserted),
983 priv->rows_reordered_handler =
984 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
985 G_CALLBACK(modest_msg_view_window_on_row_reordered),
988 if (header_view != NULL){
989 modest_header_view_add_observer(header_view,
990 MODEST_HEADER_VIEW_OBSERVER(window));
993 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
995 path = gtk_tree_row_reference_get_path (row_reference);
996 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
998 gtk_tree_model_get (priv->header_model, &iter,
999 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1001 message_reader (window, priv, header, row_reference);
1002 g_object_unref (header);
1004 gtk_tree_path_free (path);
1006 /* Check dimming rules */
1007 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1008 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1009 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1011 return MODEST_WINDOW(window);
1015 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1016 const gchar *modest_account_name,
1017 const gchar *mailbox,
1018 const gchar *msg_uid)
1020 ModestMsgViewWindow *window = NULL;
1021 ModestMsgViewWindowPrivate *priv = NULL;
1022 ModestWindowMgr *mgr = NULL;
1024 mgr = modest_runtime_get_window_mgr ();
1025 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1026 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1027 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1029 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1031 /* Remember that this is a search result,
1032 * so we can disable some UI appropriately: */
1033 priv->is_search_result = TRUE;
1035 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1037 update_window_title (window);
1038 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1039 modest_msg_view_window_update_priority (window);
1041 /* Check dimming rules */
1042 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1043 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1044 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1046 return MODEST_WINDOW(window);
1050 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1052 ModestMsgViewWindowPrivate *priv = NULL;
1054 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1055 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1057 return (priv->other_body != NULL);
1061 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1062 TnyMimePart *other_body,
1063 const gchar *modest_account_name,
1064 const gchar *mailbox,
1065 const gchar *msg_uid)
1067 GObject *obj = NULL;
1068 ModestMsgViewWindowPrivate *priv;
1069 ModestWindowMgr *mgr = NULL;
1071 g_return_val_if_fail (msg, NULL);
1072 mgr = modest_runtime_get_window_mgr ();
1073 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1074 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1075 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1076 modest_account_name, mailbox, msg_uid);
1079 priv->other_body = g_object_ref (other_body);
1080 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1082 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1084 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1086 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1088 /* Check dimming rules */
1089 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1090 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1091 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1093 return MODEST_WINDOW(obj);
1097 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1098 const gchar *modest_account_name,
1099 const gchar *mailbox,
1100 const gchar *msg_uid)
1102 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1106 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1109 ModestMsgViewWindow *window)
1111 check_dimming_rules_after_change (window);
1115 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1117 ModestMsgViewWindow *window)
1119 check_dimming_rules_after_change (window);
1121 /* The window could have dissapeared */
1124 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1126 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1127 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1131 /* On insertions we check if the folder still has the message we are
1132 * showing or do not. If do not, we do nothing. Which means we are still
1133 * not attached to any header folder and thus next/prev buttons are
1134 * still dimmed. Once the message that is shown by msg-view is found, the
1135 * new model of header-view will be attached and the references will be set.
1136 * On each further insertions dimming rules will be checked. However
1137 * this requires extra CPU time at least works.
1138 * (An message might be deleted from TnyFolder and thus will not be
1139 * inserted into the model again for example if it is removed by the
1140 * imap server and the header view is refreshed.)
1143 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1144 GtkTreePath *tree_path,
1145 GtkTreeIter *tree_iter,
1146 ModestMsgViewWindow *window)
1148 ModestMsgViewWindowPrivate *priv = NULL;
1149 TnyHeader *header = NULL;
1151 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1152 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1154 g_assert (model == priv->header_model);
1156 /* Check if the newly inserted message is the same we are actually
1157 * showing. IF not, we should remain detached from the header model
1158 * and thus prev and next toolbar buttons should remain dimmed. */
1159 gtk_tree_model_get (model, tree_iter,
1160 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1163 if (TNY_IS_HEADER (header)) {
1166 uid = modest_tny_folder_get_header_unique_id (header);
1167 if (!g_str_equal(priv->msg_uid, uid)) {
1168 check_dimming_rules_after_change (window);
1170 g_object_unref (G_OBJECT(header));
1174 g_object_unref(G_OBJECT(header));
1177 if (priv->row_reference) {
1178 gtk_tree_row_reference_free (priv->row_reference);
1181 /* Setup row_reference for the actual msg. */
1182 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1183 if (priv->row_reference == NULL) {
1184 g_warning("No reference for msg header item.");
1188 /* Now set up next_row_reference. */
1189 if (priv->next_row_reference) {
1190 gtk_tree_row_reference_free (priv->next_row_reference);
1193 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1194 select_next_valid_row (priv->header_model,
1195 &(priv->next_row_reference), FALSE, priv->is_outbox);
1197 /* Connect the remaining callbacks to become able to detect
1198 * changes in header-view. */
1199 priv->row_changed_handler =
1200 g_signal_connect (priv->header_model, "row-changed",
1201 G_CALLBACK (modest_msg_view_window_on_row_changed),
1203 priv->row_deleted_handler =
1204 g_signal_connect (priv->header_model, "row-deleted",
1205 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1207 priv->rows_reordered_handler =
1208 g_signal_connect (priv->header_model, "rows-reordered",
1209 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1212 check_dimming_rules_after_change (window);
1216 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1220 ModestMsgViewWindow *window)
1222 ModestMsgViewWindowPrivate *priv = NULL;
1223 gboolean already_changed = FALSE;
1225 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1227 /* If the current row was reordered select the proper next
1228 valid row. The same if the next row reference changes */
1229 if (priv->row_reference &&
1230 gtk_tree_row_reference_valid (priv->row_reference)) {
1232 path = gtk_tree_row_reference_get_path (priv->row_reference);
1233 if (gtk_tree_path_compare (path, arg1) == 0) {
1234 if (priv->next_row_reference) {
1235 gtk_tree_row_reference_free (priv->next_row_reference);
1237 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1238 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1239 already_changed = TRUE;
1241 gtk_tree_path_free (path);
1243 if (!already_changed &&
1244 priv->next_row_reference &&
1245 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1247 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1248 if (gtk_tree_path_compare (path, arg1) == 0) {
1249 if (priv->next_row_reference) {
1250 gtk_tree_row_reference_free (priv->next_row_reference);
1252 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1253 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1255 gtk_tree_path_free (path);
1257 check_dimming_rules_after_change (window);
1260 /* The modest_msg_view_window_update_model_replaced implements update
1261 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1262 * actually belongs to the header-view is the same as the TnyFolder of
1263 * the message of msg-view or not. If they are different, there is
1264 * nothing to do. If they are the same, then the model has replaced and
1265 * the reference in msg-view shall be replaced from the old model to
1266 * the new model. In this case the view will be detached from it's
1267 * header folder. From this point the next/prev buttons are dimmed.
1270 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1271 GtkTreeModel *model,
1272 const gchar *tny_folder_id)
1274 ModestMsgViewWindowPrivate *priv = NULL;
1275 ModestMsgViewWindow *window = NULL;
1277 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1278 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1280 window = MODEST_MSG_VIEW_WINDOW(observer);
1281 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1283 /* If there is an other folder in the header-view then we do
1284 * not care about it's model (msg list). Else if the
1285 * header-view shows the folder the msg shown by us is in, we
1286 * shall replace our model reference and make some check. */
1287 if(model == NULL || tny_folder_id == NULL ||
1288 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1291 /* Model is changed(replaced), so we should forget the old
1292 * one. Because there might be other references and there
1293 * might be some change on the model even if we unreferenced
1294 * it, we need to disconnect our signals here. */
1295 if (priv->header_model) {
1296 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1297 priv->row_changed_handler))
1298 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1299 priv->row_changed_handler);
1300 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1301 priv->row_deleted_handler))
1302 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1303 priv->row_deleted_handler);
1304 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1305 priv->row_inserted_handler))
1306 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1307 priv->row_inserted_handler);
1308 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1309 priv->rows_reordered_handler))
1310 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1311 priv->rows_reordered_handler);
1314 if (priv->row_reference)
1315 gtk_tree_row_reference_free (priv->row_reference);
1316 if (priv->next_row_reference)
1317 gtk_tree_row_reference_free (priv->next_row_reference);
1318 g_object_unref(priv->header_model);
1321 priv->row_changed_handler = 0;
1322 priv->row_deleted_handler = 0;
1323 priv->row_inserted_handler = 0;
1324 priv->rows_reordered_handler = 0;
1325 priv->next_row_reference = NULL;
1326 priv->row_reference = NULL;
1327 priv->header_model = NULL;
1330 priv->header_model = g_object_ref (model);
1332 /* Also we must connect to the new model for row insertions.
1333 * Only for insertions now. We will need other ones only after
1334 * the msg is show by msg-view is added to the new model. */
1335 priv->row_inserted_handler =
1336 g_signal_connect (priv->header_model, "row-inserted",
1337 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1340 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1341 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1345 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1347 ModestMsgViewWindowPrivate *priv= NULL;
1349 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1350 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1352 return priv->progress_hint;
1356 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1358 ModestMsgViewWindowPrivate *priv= NULL;
1360 TnyHeader *header = NULL;
1361 GtkTreePath *path = NULL;
1364 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1365 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1367 /* If the message was not obtained from a treemodel,
1368 * for instance if it was opened directly by the search UI:
1370 if (priv->header_model == NULL ||
1371 priv->row_reference == NULL ||
1372 !gtk_tree_row_reference_valid (priv->row_reference)) {
1373 msg = modest_msg_view_window_get_message (self);
1375 header = tny_msg_get_header (msg);
1376 g_object_unref (msg);
1381 /* Get iter of the currently selected message in the header view: */
1382 path = gtk_tree_row_reference_get_path (priv->row_reference);
1383 g_return_val_if_fail (path != NULL, NULL);
1384 gtk_tree_model_get_iter (priv->header_model,
1388 /* Get current message header */
1389 gtk_tree_model_get (priv->header_model, &iter,
1390 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1393 gtk_tree_path_free (path);
1398 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1400 ModestMsgViewWindowPrivate *priv;
1402 g_return_val_if_fail (self, NULL);
1404 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1406 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1410 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1412 ModestMsgViewWindowPrivate *priv;
1414 g_return_val_if_fail (self, NULL);
1416 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1418 return (const gchar*) priv->msg_uid;
1422 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1425 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1426 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1427 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1431 is_active = gtk_toggle_action_get_active (toggle);
1434 gtk_widget_show (priv->find_toolbar);
1435 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1437 gtk_widget_hide (priv->find_toolbar);
1438 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1441 /* update the toggle buttons status */
1442 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1444 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1449 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1450 ModestMsgViewWindow *obj)
1452 GtkToggleAction *toggle;
1453 ModestWindowPrivate *parent_priv;
1454 ModestMsgViewWindowPrivate *priv;
1456 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1457 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1459 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1460 gtk_toggle_action_set_active (toggle, FALSE);
1461 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1465 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1466 ModestMsgViewWindow *obj)
1468 gchar *current_search;
1469 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1471 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1472 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1476 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1478 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1479 g_free (current_search);
1480 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1484 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1486 g_free (priv->last_search);
1487 priv->last_search = g_strdup (current_search);
1488 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1491 hildon_banner_show_information (NULL, NULL,
1492 _HL("ckct_ib_find_no_matches"));
1493 g_free (priv->last_search);
1494 priv->last_search = NULL;
1496 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1499 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1500 hildon_banner_show_information (NULL, NULL,
1501 _HL("ckct_ib_find_search_complete"));
1502 g_free (priv->last_search);
1503 priv->last_search = NULL;
1505 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1509 g_free (current_search);
1514 modest_msg_view_window_set_zoom (ModestWindow *window,
1517 ModestMsgViewWindowPrivate *priv;
1518 ModestWindowPrivate *parent_priv;
1520 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1522 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1523 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1524 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1529 modest_msg_view_window_get_zoom (ModestWindow *window)
1531 ModestMsgViewWindowPrivate *priv;
1533 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1535 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1536 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1540 modest_msg_view_window_zoom_plus (ModestWindow *window)
1543 ModestMsgViewWindowPrivate *priv;
1547 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1548 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1550 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1552 if (zoom_level >= 2.0) {
1553 hildon_banner_show_information (NULL, NULL,
1554 _CS("ckct_ib_max_zoom_level_reached"));
1556 } else if (zoom_level >= 1.5) {
1558 } else if (zoom_level >= 1.2) {
1560 } else if (zoom_level >= 1.0) {
1562 } else if (zoom_level >= 0.8) {
1564 } else if (zoom_level >= 0.5) {
1570 /* set zoom level */
1571 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1572 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1573 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1574 g_free (banner_text);
1575 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1581 modest_msg_view_window_zoom_minus (ModestWindow *window)
1584 ModestMsgViewWindowPrivate *priv;
1588 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1589 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1591 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1593 if (zoom_level <= 0.5) {
1594 hildon_banner_show_information (NULL, NULL,
1595 _CS("ckct_ib_min_zoom_level_reached"));
1597 } else if (zoom_level <= 0.8) {
1599 } else if (zoom_level <= 1.0) {
1601 } else if (zoom_level <= 1.2) {
1603 } else if (zoom_level <= 1.5) {
1605 } else if (zoom_level <= 2.0) {
1611 /* set zoom level */
1612 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1613 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1614 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1615 g_free (banner_text);
1616 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1623 modest_msg_view_window_key_event (GtkWidget *window,
1629 focus = gtk_window_get_focus (GTK_WINDOW (window));
1631 /* for the find toolbar case */
1632 if (focus && GTK_IS_ENTRY (focus)) {
1633 if (event->keyval == GDK_BackSpace) {
1635 copy = gdk_event_copy ((GdkEvent *) event);
1636 gtk_widget_event (focus, copy);
1637 gdk_event_free (copy);
1642 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1643 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1644 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1645 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1646 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1647 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1648 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1649 /* gboolean return_value; */
1651 if (event->type == GDK_KEY_PRESS) {
1652 GtkScrollType scroll_type;
1654 switch (event->keyval) {
1657 scroll_type = GTK_SCROLL_STEP_UP; break;
1660 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1662 case GDK_KP_Page_Up:
1663 scroll_type = GTK_SCROLL_PAGE_UP; break;
1665 case GDK_KP_Page_Down:
1666 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1669 scroll_type = GTK_SCROLL_START; break;
1672 scroll_type = GTK_SCROLL_END; break;
1673 default: scroll_type = GTK_SCROLL_NONE;
1676 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1677 /* scroll_type, FALSE, &return_value); */
1688 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1691 ModestMsgViewWindowPrivate *priv;
1692 GtkTreeIter tmp_iter;
1693 gboolean is_last_selected;
1695 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1696 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1698 /*if no model (so no rows at all), then virtually we are the last*/
1699 if (!priv->header_model || !priv->row_reference)
1702 if (!gtk_tree_row_reference_valid (priv->row_reference))
1705 path = gtk_tree_row_reference_get_path (priv->row_reference);
1709 is_last_selected = TRUE;
1710 while (is_last_selected) {
1712 gtk_tree_path_next (path);
1713 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1715 gtk_tree_model_get (priv->header_model, &tmp_iter,
1716 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1719 if (msg_is_visible (header, priv->is_outbox))
1720 is_last_selected = FALSE;
1721 g_object_unref(G_OBJECT(header));
1724 gtk_tree_path_free (path);
1725 return is_last_selected;
1729 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1731 ModestMsgViewWindowPrivate *priv;
1733 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1734 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1736 return priv->header_model != NULL;
1740 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1742 ModestMsgViewWindowPrivate *priv;
1744 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1745 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1747 return priv->is_search_result;
1751 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1753 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1755 if (!check_outbox) {
1758 ModestTnySendQueueStatus status;
1759 status = modest_tny_all_send_queues_get_msg_status (header);
1760 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1761 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1766 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1769 ModestMsgViewWindowPrivate *priv;
1770 gboolean is_first_selected;
1771 GtkTreeIter tmp_iter;
1773 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1774 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1776 /*if no model (so no rows at all), then virtually we are the first*/
1777 if (!priv->header_model || !priv->row_reference)
1780 if (!gtk_tree_row_reference_valid (priv->row_reference))
1783 path = gtk_tree_row_reference_get_path (priv->row_reference);
1787 is_first_selected = TRUE;
1788 while (is_first_selected) {
1790 if(!gtk_tree_path_prev (path))
1792 /* Here the 'if' is needless for logic, but let make sure
1793 * iter is valid for gtk_tree_model_get. */
1794 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1796 gtk_tree_model_get (priv->header_model, &tmp_iter,
1797 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1800 if (msg_is_visible (header, priv->is_outbox))
1801 is_first_selected = FALSE;
1802 g_object_unref(G_OBJECT(header));
1805 gtk_tree_path_free (path);
1806 return is_first_selected;
1811 GtkTreeRowReference *row_reference;
1815 message_reader_performer (gboolean canceled,
1817 GtkWindow *parent_window,
1818 TnyAccount *account,
1821 ModestMailOperation *mail_op = NULL;
1822 MsgReaderInfo *info;
1824 info = (MsgReaderInfo *) user_data;
1825 if (canceled || err) {
1826 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1830 /* Register the header - it'll be unregistered in the callback */
1831 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1833 /* New mail operation */
1834 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1835 modest_ui_actions_disk_operations_error_handler,
1838 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1839 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1840 g_object_unref (mail_op);
1842 /* Update dimming rules */
1843 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1844 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1847 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1848 g_object_unref (info->header);
1849 g_slice_free (MsgReaderInfo, info);
1854 * Reads the message whose summary item is @header. It takes care of
1855 * several things, among others:
1857 * If the message was not previously downloaded then ask the user
1858 * before downloading. If there is no connection launch the connection
1859 * dialog. Update toolbar dimming rules.
1861 * Returns: TRUE if the mail operation was started, otherwise if the
1862 * user do not want to download the message, or if the user do not
1863 * want to connect, then the operation is not issued
1866 message_reader (ModestMsgViewWindow *window,
1867 ModestMsgViewWindowPrivate *priv,
1869 GtkTreeRowReference *row_reference)
1871 ModestWindowMgr *mgr;
1872 TnyAccount *account;
1874 MsgReaderInfo *info;
1876 g_return_val_if_fail (row_reference != NULL, FALSE);
1878 /* We set the header from model while we're loading */
1879 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1880 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1882 mgr = modest_runtime_get_window_mgr ();
1883 /* Msg download completed */
1884 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1886 /* Ask the user if he wants to download the message if
1888 if (!tny_device_is_online (modest_runtime_get_device())) {
1889 GtkResponseType response;
1891 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1892 _("mcen_nc_get_msg"));
1893 if (response == GTK_RESPONSE_CANCEL) {
1894 update_window_title (window);
1898 folder = tny_header_get_folder (header);
1899 info = g_slice_new (MsgReaderInfo);
1900 info->header = g_object_ref (header);
1901 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1903 /* Offer the connection dialog if necessary */
1904 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1906 TNY_FOLDER_STORE (folder),
1907 message_reader_performer,
1909 g_object_unref (folder);
1914 folder = tny_header_get_folder (header);
1915 account = tny_folder_get_account (folder);
1916 info = g_slice_new (MsgReaderInfo);
1917 info->header = g_object_ref (header);
1918 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1920 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1921 g_object_unref (account);
1922 g_object_unref (folder);
1928 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1930 ModestMsgViewWindowPrivate *priv;
1931 GtkTreePath *path= NULL;
1932 GtkTreeIter tmp_iter;
1934 gboolean retval = TRUE;
1935 GtkTreeRowReference *row_reference = NULL;
1937 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1938 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1940 if (!priv->row_reference)
1943 /* Update the next row reference if it's not valid. This could
1944 happen if for example the header which it was pointing to,
1945 was deleted. The best place to do it is in the row-deleted
1946 handler but the tinymail model do not work like the glib
1947 tree models and reports the deletion when the row is still
1949 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1950 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1951 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1952 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1955 if (priv->next_row_reference)
1956 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1960 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1962 gtk_tree_model_get_iter (priv->header_model,
1965 gtk_tree_path_free (path);
1967 gtk_tree_model_get (priv->header_model, &tmp_iter,
1968 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1971 /* Read the message & show it */
1972 if (!message_reader (window, priv, header, row_reference)) {
1975 gtk_tree_row_reference_free (row_reference);
1978 g_object_unref (header);
1984 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1986 ModestMsgViewWindowPrivate *priv = NULL;
1988 gboolean finished = FALSE;
1989 gboolean retval = FALSE;
1991 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1992 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1994 /* Return inmediatly if there is no header model */
1995 if (!priv->header_model || !priv->row_reference)
1998 path = gtk_tree_row_reference_get_path (priv->row_reference);
1999 while (!finished && gtk_tree_path_prev (path)) {
2003 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2004 gtk_tree_model_get (priv->header_model, &iter,
2005 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2009 if (msg_is_visible (header, priv->is_outbox)) {
2010 GtkTreeRowReference *row_reference;
2011 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2012 /* Read the message & show it */
2013 retval = message_reader (window, priv, header, row_reference);
2014 gtk_tree_row_reference_free (row_reference);
2018 g_object_unref (header);
2022 gtk_tree_path_free (path);
2027 view_msg_cb (ModestMailOperation *mail_op,
2034 ModestMsgViewWindow *self = NULL;
2035 ModestMsgViewWindowPrivate *priv = NULL;
2036 GtkTreeRowReference *row_reference = NULL;
2038 /* Unregister the header (it was registered before creating the mail operation) */
2039 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2041 row_reference = (GtkTreeRowReference *) user_data;
2043 gtk_tree_row_reference_free (row_reference);
2044 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2046 /* Restore window title */
2047 update_window_title (self);
2048 g_object_unref (self);
2053 /* If there was any error */
2054 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2055 gtk_tree_row_reference_free (row_reference);
2056 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2058 /* Restore window title */
2059 update_window_title (self);
2060 g_object_unref (self);
2065 /* Get the window */
2066 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2067 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2068 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2070 /* Update the row reference */
2071 if (priv->row_reference != NULL) {
2072 gtk_tree_row_reference_free (priv->row_reference);
2073 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2074 if (priv->next_row_reference != NULL) {
2075 gtk_tree_row_reference_free (priv->next_row_reference);
2077 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2078 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2081 /* Mark header as read */
2082 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2083 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2085 /* Set new message */
2086 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2087 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2088 modest_msg_view_window_update_priority (self);
2089 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2090 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2093 /* Set the new message uid of the window */
2094 if (priv->msg_uid) {
2095 g_free (priv->msg_uid);
2096 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2099 /* Notify the observers */
2100 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2101 0, priv->header_model, priv->row_reference);
2104 g_object_unref (self);
2105 gtk_tree_row_reference_free (row_reference);
2109 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2111 ModestMsgViewWindowPrivate *priv;
2113 TnyFolderType folder_type;
2115 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2117 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2119 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2123 folder = tny_msg_get_folder (msg);
2125 folder_type = modest_tny_folder_guess_folder_type (folder);
2126 g_object_unref (folder);
2128 g_object_unref (msg);
2136 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2138 ModestMsgViewWindowPrivate *priv;
2139 TnyHeader *header = NULL;
2140 TnyHeaderFlags flags = 0;
2142 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2144 if (priv->header_model && priv->row_reference) {
2146 GtkTreePath *path = NULL;
2148 path = gtk_tree_row_reference_get_path (priv->row_reference);
2149 g_return_if_fail (path != NULL);
2150 gtk_tree_model_get_iter (priv->header_model,
2152 gtk_tree_row_reference_get_path (priv->row_reference));
2154 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2156 gtk_tree_path_free (path);
2159 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2161 header = tny_msg_get_header (msg);
2162 g_object_unref (msg);
2167 flags = tny_header_get_flags (header);
2168 g_object_unref(G_OBJECT(header));
2171 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2176 toolbar_resize (ModestMsgViewWindow *self)
2178 ModestMsgViewWindowPrivate *priv = NULL;
2179 ModestWindowPrivate *parent_priv = NULL;
2181 gint static_button_size;
2182 ModestWindowMgr *mgr;
2184 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2185 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2186 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2188 mgr = modest_runtime_get_window_mgr ();
2189 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2191 if (parent_priv->toolbar) {
2192 /* left size buttons */
2193 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2194 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2195 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2196 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2197 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2198 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2199 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2200 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2201 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2202 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2203 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2204 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2205 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2206 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2207 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2208 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2210 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2211 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2212 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2213 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2218 modest_msg_view_window_show_toolbar (ModestWindow *self,
2219 gboolean show_toolbar)
2221 ModestMsgViewWindowPrivate *priv = NULL;
2222 ModestWindowPrivate *parent_priv;
2224 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2225 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2227 /* Set optimized view status */
2228 priv->optimized_view = !show_toolbar;
2230 if (!parent_priv->toolbar) {
2231 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2233 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2234 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2236 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2237 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2238 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2241 hildon_window_add_toolbar (HILDON_WINDOW (self),
2242 GTK_TOOLBAR (parent_priv->toolbar));
2247 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2248 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2249 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2251 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2252 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2253 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2255 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2258 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2259 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2264 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2266 ModestMsgViewWindow *window)
2268 if (!GTK_WIDGET_VISIBLE (window))
2271 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2275 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2277 ModestMsgViewWindowPrivate *priv;
2279 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2280 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2282 return priv->progress_hint;
2286 observers_empty (ModestMsgViewWindow *self)
2289 ModestMsgViewWindowPrivate *priv;
2290 gboolean is_empty = TRUE;
2291 guint pending_ops = 0;
2293 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2294 tmp = priv->progress_widgets;
2296 /* Check all observers */
2297 while (tmp && is_empty) {
2298 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2299 is_empty = pending_ops == 0;
2301 tmp = g_slist_next(tmp);
2308 on_account_removed (TnyAccountStore *account_store,
2309 TnyAccount *account,
2312 /* Do nothing if it's a transport account, because we only
2313 show the messages of a store account */
2314 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2315 const gchar *parent_acc = NULL;
2316 const gchar *our_acc = NULL;
2318 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2319 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2321 /* Close this window if I'm showing a message of the removed account */
2322 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2323 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2328 on_mail_operation_started (ModestMailOperation *mail_op,
2331 ModestMsgViewWindow *self;
2332 ModestMailOperationTypeOperation op_type;
2334 ModestMsgViewWindowPrivate *priv;
2335 GObject *source = NULL;
2337 self = MODEST_MSG_VIEW_WINDOW (user_data);
2338 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2339 op_type = modest_mail_operation_get_type_operation (mail_op);
2340 tmp = priv->progress_widgets;
2341 source = modest_mail_operation_get_source(mail_op);
2342 if (G_OBJECT (self) == source) {
2343 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2344 set_toolbar_transfer_mode(self);
2346 modest_progress_object_add_operation (
2347 MODEST_PROGRESS_OBJECT (tmp->data),
2349 tmp = g_slist_next (tmp);
2353 g_object_unref (source);
2357 on_mail_operation_finished (ModestMailOperation *mail_op,
2360 ModestMsgViewWindow *self;
2361 ModestMailOperationTypeOperation op_type;
2363 ModestMsgViewWindowPrivate *priv;
2365 self = MODEST_MSG_VIEW_WINDOW (user_data);
2366 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2367 op_type = modest_mail_operation_get_type_operation (mail_op);
2368 tmp = priv->progress_widgets;
2370 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2372 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2374 tmp = g_slist_next (tmp);
2377 /* If no more operations are being observed, NORMAL mode is enabled again */
2378 if (observers_empty (self)) {
2379 set_progress_hint (self, FALSE);
2383 /* Update dimming rules. We have to do this right here
2384 and not in view_msg_cb because at that point the
2385 transfer mode is still enabled so the dimming rule
2386 won't let the user delete the message that has been
2387 readed for example */
2388 check_dimming_rules_after_change (self);
2393 on_queue_changed (ModestMailOperationQueue *queue,
2394 ModestMailOperation *mail_op,
2395 ModestMailOperationQueueNotification type,
2396 ModestMsgViewWindow *self)
2398 ModestMsgViewWindowPrivate *priv;
2400 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2402 /* If this operations was created by another window, do nothing */
2403 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2406 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2407 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2409 "operation-started",
2410 G_CALLBACK (on_mail_operation_started),
2412 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2414 "operation-finished",
2415 G_CALLBACK (on_mail_operation_finished),
2417 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2418 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2420 "operation-started");
2421 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2423 "operation-finished");
2428 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2430 ModestMsgViewWindowPrivate *priv;
2431 TnyList *selected_attachments = NULL;
2433 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2434 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2436 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2437 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2439 return selected_attachments;
2443 ModestMsgViewWindow *self;
2445 } DecodeAsyncHelper;
2448 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2454 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2456 /* It could happen that the window was closed */
2457 if (GTK_WIDGET_VISIBLE (helper->self))
2458 set_progress_hint (helper->self, FALSE);
2460 if (cancelled || err) {
2462 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2463 modest_platform_information_banner (NULL, NULL, msg);
2469 /* make the file read-only */
2470 g_chmod(helper->file_path, 0444);
2472 /* Activate the file */
2473 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2477 g_object_unref (helper->self);
2478 g_free (helper->file_path);
2479 g_slice_free (DecodeAsyncHelper, helper);
2483 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2484 TnyMimePart *mime_part)
2486 ModestMsgViewWindowPrivate *priv;
2487 const gchar *msg_uid;
2488 gchar *attachment_uid = NULL;
2489 gint attachment_index = 0;
2490 TnyList *attachments;
2492 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2493 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2494 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2496 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2497 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2498 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2499 g_object_unref (attachments);
2501 if (msg_uid && attachment_index >= 0) {
2502 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2505 if (mime_part == NULL) {
2506 gboolean error = FALSE;
2507 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2508 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2510 } else if (tny_list_get_length (selected_attachments) > 1) {
2511 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2515 iter = tny_list_create_iterator (selected_attachments);
2516 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2517 g_object_unref (iter);
2519 if (selected_attachments)
2520 g_object_unref (selected_attachments);
2525 g_object_ref (mime_part);
2528 if (tny_mime_part_is_purged (mime_part))
2531 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2532 gchar *filepath = NULL;
2533 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2534 gboolean show_error_banner = FALSE;
2535 TnyFsStream *temp_stream = NULL;
2536 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2539 if (temp_stream != NULL) {
2540 DecodeAsyncHelper *helper;
2542 /* Activate progress hint */
2543 set_progress_hint (window, TRUE);
2545 helper = g_slice_new0 (DecodeAsyncHelper);
2546 helper->self = g_object_ref (window);
2547 helper->file_path = g_strdup (filepath);
2549 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2550 on_decode_to_stream_async_handler,
2553 g_object_unref (temp_stream);
2554 /* NOTE: files in the temporary area will be automatically
2555 * cleaned after some time if they are no longer in use */
2558 const gchar *content_type;
2559 /* the file may already exist but it isn't writable,
2560 * let's try to open it anyway */
2561 content_type = tny_mime_part_get_content_type (mime_part);
2562 modest_platform_activate_file (filepath, content_type);
2564 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2565 show_error_banner = TRUE;
2570 if (show_error_banner)
2571 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2572 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2573 ModestWindowMgr *mgr;
2574 ModestWindow *msg_win = NULL;
2575 TnyMsg *current_msg;
2579 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2580 mgr = modest_runtime_get_window_mgr ();
2581 header = tny_msg_get_header (TNY_MSG (current_msg));
2582 found = modest_window_mgr_find_registered_message_uid (mgr,
2587 g_warning ("window for this body is already being created");
2590 /* it's not found, so create a new window for it */
2591 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2592 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2593 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2595 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2597 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2598 account, mailbox, attachment_uid);
2600 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2601 modest_window_get_zoom (MODEST_WINDOW (window)));
2602 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2603 gtk_widget_show_all (GTK_WIDGET (msg_win));
2605 gtk_widget_destroy (GTK_WIDGET (msg_win));
2607 g_object_unref (current_msg);
2609 /* message attachment */
2610 TnyHeader *header = NULL;
2611 ModestWindowMgr *mgr;
2612 ModestWindow *msg_win = NULL;
2615 header = tny_msg_get_header (TNY_MSG (mime_part));
2616 mgr = modest_runtime_get_window_mgr ();
2617 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2620 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2621 * thus, we don't do anything */
2622 g_warning ("window for is already being created");
2624 /* it's not found, so create a new window for it */
2625 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2626 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2627 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2629 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2630 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2631 mailbox, attachment_uid);
2632 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2633 modest_window_get_zoom (MODEST_WINDOW (window)));
2634 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2635 gtk_widget_show_all (GTK_WIDGET (msg_win));
2637 gtk_widget_destroy (GTK_WIDGET (msg_win));
2643 g_free (attachment_uid);
2645 g_object_unref (mime_part);
2657 GnomeVFSResult result;
2660 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2661 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2662 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2663 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2666 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2670 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2671 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2672 g_free (pair->filename);
2673 g_object_unref (pair->part);
2674 g_slice_free (SaveMimePartPair, pair);
2676 g_list_free (info->pairs);
2679 g_slice_free (SaveMimePartInfo, info);
2684 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2686 if (info->pairs != NULL) {
2687 save_mime_part_to_file (info);
2689 /* This is a GDK lock because we are an idle callback and
2690 * hildon_banner_show_information is or does Gtk+ code */
2692 gdk_threads_enter (); /* CHECKED */
2693 save_mime_part_info_free (info, TRUE);
2694 if (info->result == GNOME_VFS_OK) {
2695 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2696 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2697 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2698 modest_platform_information_banner (NULL, NULL, msg);
2701 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2703 gdk_threads_leave (); /* CHECKED */
2710 save_mime_part_to_file (SaveMimePartInfo *info)
2712 GnomeVFSHandle *handle;
2714 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2716 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2717 if (info->result == GNOME_VFS_OK) {
2718 GError *error = NULL;
2719 stream = tny_vfs_stream_new (handle);
2720 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2721 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2723 if ((error->domain == TNY_ERROR_DOMAIN) &&
2724 (error->code == TNY_IO_ERROR_WRITE) &&
2725 (errno == ENOSPC)) {
2726 info->result = GNOME_VFS_ERROR_NO_SPACE;
2728 info->result = GNOME_VFS_ERROR_IO;
2731 g_object_unref (G_OBJECT (stream));
2732 g_object_unref (pair->part);
2733 g_slice_free (SaveMimePartPair, pair);
2734 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2736 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2737 save_mime_part_info_free (info, FALSE);
2740 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2745 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2747 gboolean is_ok = TRUE;
2748 gint replaced_files = 0;
2749 const GList *files = info->pairs;
2752 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2753 SaveMimePartPair *pair = iter->data;
2754 if (modest_utils_file_exists (pair->filename)) {
2758 if (replaced_files) {
2759 GtkWidget *confirm_overwrite_dialog;
2761 if (replaced_files == 1) {
2762 SaveMimePartPair *pair = files->data;
2763 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2765 gchar *message = g_strdup_printf ("%s\n%s",
2766 _FM("docm_nc_replace_file"),
2767 (basename) ? basename : "");
2768 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2771 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL,
2772 _FM("docm_nc_replace_multiple"));
2774 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK)
2777 gtk_widget_destroy (confirm_overwrite_dialog);
2781 save_mime_part_info_free (info, TRUE);
2783 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2789 save_attachments_response (GtkDialog *dialog,
2793 TnyList *mime_parts;
2795 GList *files_to_save = NULL;
2796 gchar *current_folder;
2798 mime_parts = TNY_LIST (user_data);
2800 if (arg1 != GTK_RESPONSE_OK)
2803 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2804 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2805 if (current_folder && current_folder != '\0') {
2807 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2808 current_folder,&err);
2810 g_debug ("Error storing latest used folder: %s", err->message);
2814 g_free (current_folder);
2816 if (!modest_utils_folder_writable (chooser_uri)) {
2817 hildon_banner_show_information
2818 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2822 iter = tny_list_create_iterator (mime_parts);
2823 while (!tny_iterator_is_done (iter)) {
2824 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2826 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2827 !tny_mime_part_is_purged (mime_part) &&
2828 (tny_mime_part_get_filename (mime_part) != NULL)) {
2829 SaveMimePartPair *pair;
2831 pair = g_slice_new0 (SaveMimePartPair);
2833 if (tny_list_get_length (mime_parts) > 1) {
2835 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2836 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2839 pair->filename = g_strdup (chooser_uri);
2841 pair->part = mime_part;
2842 files_to_save = g_list_prepend (files_to_save, pair);
2844 tny_iterator_next (iter);
2846 g_object_unref (iter);
2848 g_free (chooser_uri);
2850 if (files_to_save != NULL) {
2851 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2852 info->pairs = files_to_save;
2853 info->result = TRUE;
2854 save_mime_parts_to_file_with_checks (info);
2858 /* Free and close the dialog */
2859 g_object_unref (mime_parts);
2860 gtk_widget_destroy (GTK_WIDGET (dialog));
2864 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2865 TnyList *mime_parts)
2867 ModestMsgViewWindowPrivate *priv;
2868 GtkWidget *save_dialog = NULL;
2869 gchar *conf_folder = NULL;
2870 gchar *filename = NULL;
2871 gchar *save_multiple_str = NULL;
2873 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2874 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2876 if (mime_parts == NULL) {
2877 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2878 * selection available */
2879 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2880 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2881 g_object_unref (mime_parts);
2884 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2886 g_object_unref (mime_parts);
2892 g_object_ref (mime_parts);
2895 /* prepare dialog */
2896 if (tny_list_get_length (mime_parts) == 1) {
2898 /* only one attachment selected */
2899 iter = tny_list_create_iterator (mime_parts);
2900 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2901 g_object_unref (iter);
2902 if (!modest_tny_mime_part_is_msg (mime_part) &&
2903 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2904 !tny_mime_part_is_purged (mime_part)) {
2905 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2907 /* TODO: show any error? */
2908 g_warning ("Tried to save a non-file attachment");
2909 g_object_unref (mime_parts);
2912 g_object_unref (mime_part);
2914 gint num = tny_list_get_length (mime_parts);
2915 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
2916 "sfil_va_number_of_objects_attachment",
2917 "sfil_va_number_of_objects_attachments",
2921 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2922 GTK_FILE_CHOOSER_ACTION_SAVE);
2925 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2926 if (conf_folder && conf_folder[0] != '\0') {
2927 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2930 /* Set the default folder to images folder */
2931 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2932 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2933 g_free (docs_folder);
2935 g_free (conf_folder);
2939 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2944 /* if multiple, set multiple string */
2945 if (save_multiple_str) {
2946 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2947 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2950 /* We must run this asynchronously, because the hildon dialog
2951 performs a gtk_dialog_run by itself which leads to gdk
2953 g_signal_connect (save_dialog, "response",
2954 G_CALLBACK (save_attachments_response), mime_parts);
2956 gtk_widget_show_all (save_dialog);
2960 show_remove_attachment_information (gpointer userdata)
2962 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2963 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2965 /* We're outside the main lock */
2966 gdk_threads_enter ();
2968 if (priv->remove_attachment_banner != NULL) {
2969 gtk_widget_destroy (priv->remove_attachment_banner);
2970 g_object_unref (priv->remove_attachment_banner);
2973 priv->remove_attachment_banner = g_object_ref (
2974 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2976 gdk_threads_leave ();
2982 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2984 ModestMsgViewWindowPrivate *priv;
2985 TnyList *mime_parts = NULL, *tmp;
2986 gchar *confirmation_message;
2992 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2993 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2995 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2996 * because we don't have selection
2998 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3000 /* Remove already purged messages from mime parts list. We use
3001 a copy of the list to remove items in the original one */
3002 tmp = tny_list_copy (mime_parts);
3003 iter = tny_list_create_iterator (tmp);
3004 while (!tny_iterator_is_done (iter)) {
3005 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3006 if (tny_mime_part_is_purged (part))
3007 tny_list_remove (mime_parts, (GObject *) part);
3009 g_object_unref (part);
3010 tny_iterator_next (iter);
3012 g_object_unref (tmp);
3013 g_object_unref (iter);
3015 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3016 tny_list_get_length (mime_parts) == 0) {
3017 g_object_unref (mime_parts);
3021 n_attachments = tny_list_get_length (mime_parts);
3022 if (n_attachments == 1) {
3026 iter = tny_list_create_iterator (mime_parts);
3027 part = (TnyMimePart *) tny_iterator_get_current (iter);
3028 g_object_unref (iter);
3029 if (modest_tny_mime_part_is_msg (part)) {
3031 header = tny_msg_get_header (TNY_MSG (part));
3032 filename = tny_header_dup_subject (header);
3033 g_object_unref (header);
3034 if (filename == NULL)
3035 filename = g_strdup (_("mail_va_no_subject"));
3037 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3039 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3041 g_object_unref (part);
3043 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3044 "mcen_nc_purge_files_text",
3045 n_attachments), n_attachments);
3047 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3048 confirmation_message);
3049 g_free (confirmation_message);
3051 if (response != GTK_RESPONSE_OK) {
3052 g_object_unref (mime_parts);
3056 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3058 iter = tny_list_create_iterator (mime_parts);
3059 while (!tny_iterator_is_done (iter)) {
3062 part = (TnyMimePart *) tny_iterator_get_current (iter);
3063 tny_mime_part_set_purged (TNY_MIME_PART (part));
3064 g_object_unref (part);
3065 tny_iterator_next (iter);
3067 g_object_unref (iter);
3069 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3070 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3071 tny_msg_rewrite_cache (msg);
3072 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3073 g_object_unref (msg);
3075 g_object_unref (mime_parts);
3077 if (priv->purge_timeout > 0) {
3078 g_source_remove (priv->purge_timeout);
3079 priv->purge_timeout = 0;
3082 if (priv->remove_attachment_banner) {
3083 gtk_widget_destroy (priv->remove_attachment_banner);
3084 g_object_unref (priv->remove_attachment_banner);
3085 priv->remove_attachment_banner = NULL;
3091 update_window_title (ModestMsgViewWindow *window)
3093 ModestMsgViewWindowPrivate *priv;
3095 TnyHeader *header = NULL;
3096 gchar *subject = NULL;
3098 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3100 /* Note that if the window is closed while we're retrieving
3101 the message, this widget could de deleted */
3102 if (!priv->msg_view)
3105 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3107 if (priv->other_body) {
3110 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3112 g_strstrip (description);
3113 subject = description;
3115 } else if (msg != NULL) {
3116 header = tny_msg_get_header (msg);
3117 subject = tny_header_dup_subject (header);
3118 g_object_unref (header);
3119 g_object_unref (msg);
3122 if ((subject == NULL)||(subject[0] == '\0')) {
3124 subject = g_strdup (_("mail_va_no_subject"));
3127 gtk_window_set_title (GTK_WINDOW (window), subject);
3132 on_move_focus (GtkWidget *widget,
3133 GtkDirectionType direction,
3136 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3140 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3142 GnomeVFSResult result;
3143 GnomeVFSHandle *handle = NULL;
3144 GnomeVFSFileInfo *info = NULL;
3147 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3148 if (result != GNOME_VFS_OK) {
3153 info = gnome_vfs_file_info_new ();
3154 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3155 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3156 /* We put a "safe" default size for going to cache */
3157 *expected_size = (300*1024);
3159 *expected_size = info->size;
3161 gnome_vfs_file_info_unref (info);
3163 stream = tny_vfs_stream_new (handle);
3172 TnyStream *output_stream;
3173 GtkWidget *msg_view;
3178 on_fetch_image_idle_refresh_view (gpointer userdata)
3181 FetchImageData *fidata = (FetchImageData *) userdata;
3183 gdk_threads_enter ();
3184 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3185 ModestMsgViewWindowPrivate *priv;
3187 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3188 priv->fetching_images--;
3189 gtk_widget_queue_draw (fidata->msg_view);
3190 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3192 gdk_threads_leave ();
3194 g_object_unref (fidata->msg_view);
3195 g_object_unref (fidata->window);
3196 g_slice_free (FetchImageData, fidata);
3201 on_fetch_image_thread (gpointer userdata)
3203 FetchImageData *fidata = (FetchImageData *) userdata;
3204 TnyStreamCache *cache;
3205 TnyStream *cache_stream;
3207 cache = modest_runtime_get_images_cache ();
3209 tny_stream_cache_get_stream (cache,
3211 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3212 (gpointer) fidata->uri);
3213 g_free (fidata->cache_id);
3214 g_free (fidata->uri);
3216 if (cache_stream != NULL) {
3219 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3222 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3223 if (G_UNLIKELY (nb_read < 0)) {
3225 } else if (G_LIKELY (nb_read > 0)) {
3226 gssize nb_written = 0;
3228 while (G_UNLIKELY (nb_written < nb_read)) {
3231 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3232 nb_read - nb_written);
3233 if (G_UNLIKELY (len < 0))
3239 tny_stream_close (cache_stream);
3240 g_object_unref (cache_stream);
3243 tny_stream_close (fidata->output_stream);
3244 g_object_unref (fidata->output_stream);
3246 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3252 on_fetch_image (ModestMsgView *msgview,
3255 ModestMsgViewWindow *window)
3257 const gchar *current_account;
3258 ModestMsgViewWindowPrivate *priv;
3259 FetchImageData *fidata;
3261 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3263 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3265 fidata = g_slice_new0 (FetchImageData);
3266 fidata->msg_view = g_object_ref (msgview);
3267 fidata->window = g_object_ref (window);
3268 fidata->uri = g_strdup (uri);
3269 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3270 fidata->output_stream = g_object_ref (stream);
3272 priv->fetching_images++;
3273 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3274 g_object_unref (fidata->output_stream);
3275 g_free (fidata->cache_id);
3276 g_free (fidata->uri);
3277 g_object_unref (fidata->msg_view);
3278 g_slice_free (FetchImageData, fidata);
3279 tny_stream_close (stream);
3280 priv->fetching_images--;
3281 update_progress_hint (window);
3284 update_progress_hint (window);
3290 setup_menu (ModestMsgViewWindow *self)
3292 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3294 /* Settings menu buttons */
3295 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3296 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3297 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3298 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3299 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3300 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3302 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3303 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3304 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3305 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3306 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3307 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3309 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3310 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3311 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3312 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3313 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3314 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3316 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3317 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3318 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3319 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3320 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3321 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3323 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mail_bd_external_images"), NULL,
3324 APP_MENU_CALLBACK (modest_ui_actions_on_fetch_images),
3325 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_fetch_images));
3326 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3327 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3328 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3332 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3334 ModestMsgViewWindowPrivate *priv;
3335 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3336 GSList *recipients = NULL;
3338 gboolean contacts_to_add = FALSE;
3340 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3344 header = modest_msg_view_window_get_header (self);
3347 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3348 g_object_unref (header);
3350 recipients = modest_tny_msg_get_all_recipients_list (msg);
3351 g_object_unref (msg);
3354 if (recipients != NULL) {
3355 GtkWidget *picker_dialog;
3356 GtkWidget *selector;
3358 gchar *selected = NULL;
3360 selector = hildon_touch_selector_new_text ();
3361 g_object_ref (selector);
3363 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3364 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3365 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3366 (const gchar *) node->data);
3367 contacts_to_add = TRUE;
3371 if (contacts_to_add) {
3374 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3375 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3377 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3378 HILDON_TOUCH_SELECTOR (selector));
3380 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3382 if (picker_result == GTK_RESPONSE_OK) {
3383 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3385 gtk_widget_destroy (picker_dialog);
3388 modest_address_book_add_address (selected);
3393 g_object_unref (selector);
3398 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3402 _modest_msg_view_window_map_event (GtkWidget *widget,
3406 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3408 update_progress_hint (self);
3414 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3416 ModestMsgViewWindowPrivate *priv;
3417 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3419 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3423 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3425 ModestMsgViewWindowPrivate *priv;
3426 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3428 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3430 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3434 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3436 ModestMsgViewWindowPrivate *priv;
3439 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3441 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3442 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3444 if (!message_reader (self, priv, header, priv->row_reference)) {
3445 g_warning ("Shouldn't happen, trying to reload a message failed");
3448 g_object_unref (header);