1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
69 #include <modest-account-protocol.h>
71 #define MYDOCS_ENV "MYDOCSDIR"
72 #define DOCS_FOLDER ".documents"
74 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
75 struct _ModestMsgViewWindowPrivate {
78 GtkWidget *main_scroll;
79 GtkWidget *find_toolbar;
82 /* Progress observers */
83 GSList *progress_widgets;
86 GtkWidget *prev_toolitem;
87 GtkWidget *next_toolitem;
88 gboolean progress_hint;
91 /* Optimized view enabled */
92 gboolean optimized_view;
94 /* Whether this was created via the *_new_for_search_result() function. */
95 gboolean is_search_result;
97 /* Whether the message is in outbox */
100 /* A reference to the @model of the header view
101 * to allow selecting previous/next messages,
102 * if the message is currently selected in the header view.
104 const gchar *header_folder_id;
105 GtkTreeModel *header_model;
106 GtkTreeRowReference *row_reference;
107 GtkTreeRowReference *next_row_reference;
109 gulong clipboard_change_handler;
110 gulong queue_change_handler;
111 gulong account_removed_handler;
112 gulong row_changed_handler;
113 gulong row_deleted_handler;
114 gulong row_inserted_handler;
115 gulong rows_reordered_handler;
118 GtkWidget *remove_attachment_banner;
121 TnyMimePart *other_body;
126 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
127 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
128 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
129 static void modest_msg_view_window_finalize (GObject *obj);
130 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
131 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
132 ModestMsgViewWindow *obj);
133 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
134 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
137 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
139 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
140 static void modest_msg_view_window_set_zoom (ModestWindow *window,
142 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
143 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
144 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
147 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
149 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
150 gboolean show_toolbar);
152 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
154 ModestMsgViewWindow *window);
156 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
159 ModestMsgViewWindow *window);
161 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
163 ModestMsgViewWindow *window);
165 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
166 GtkTreePath *tree_path,
167 GtkTreeIter *tree_iter,
168 ModestMsgViewWindow *window);
170 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
174 ModestMsgViewWindow *window);
176 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
178 const gchar *tny_folder_id);
180 static void on_queue_changed (ModestMailOperationQueue *queue,
181 ModestMailOperation *mail_op,
182 ModestMailOperationQueueNotification type,
183 ModestMsgViewWindow *self);
185 static void on_account_removed (TnyAccountStore *account_store,
189 static void on_move_focus (GtkWidget *widget,
190 GtkDirectionType direction,
193 static void view_msg_cb (ModestMailOperation *mail_op,
200 static void set_progress_hint (ModestMsgViewWindow *self,
203 static void update_window_title (ModestMsgViewWindow *window);
205 static void init_window (ModestMsgViewWindow *obj);
207 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
209 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
211 static gboolean on_fetch_image (ModestMsgView *msgview,
214 ModestMsgViewWindow *window);
216 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
217 GtkScrollType scroll_type,
220 static gboolean message_reader (ModestMsgViewWindow *window,
221 ModestMsgViewWindowPrivate *priv,
223 const gchar *msg_uid,
225 GtkTreeRowReference *row_reference);
227 static void setup_menu (ModestMsgViewWindow *self);
228 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
231 static void update_branding (ModestMsgViewWindow *self);
234 /* list my signals */
241 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
242 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
245 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
246 MODEST_TYPE_MSG_VIEW_WINDOW, \
247 ModestMsgViewWindowPrivate))
249 static GtkWindowClass *parent_class = NULL;
251 /* uncomment the following if you have defined any signals */
252 static guint signals[LAST_SIGNAL] = {0};
255 modest_msg_view_window_get_type (void)
257 static GType my_type = 0;
259 static const GTypeInfo my_info = {
260 sizeof(ModestMsgViewWindowClass),
261 NULL, /* base init */
262 NULL, /* base finalize */
263 (GClassInitFunc) modest_msg_view_window_class_init,
264 NULL, /* class finalize */
265 NULL, /* class data */
266 sizeof(ModestMsgViewWindow),
268 (GInstanceInitFunc) modest_msg_view_window_init,
271 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
272 "ModestMsgViewWindow",
275 static const GInterfaceInfo modest_header_view_observer_info =
277 (GInterfaceInitFunc) modest_header_view_observer_init,
278 NULL, /* interface_finalize */
279 NULL /* interface_data */
282 g_type_add_interface_static (my_type,
283 MODEST_TYPE_HEADER_VIEW_OBSERVER,
284 &modest_header_view_observer_info);
290 save_state (ModestWindow *self)
292 modest_widget_memory_save (modest_runtime_get_conf (),
294 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
298 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
299 GtkScrollType scroll_type,
303 ModestMsgViewWindowPrivate *priv;
304 gboolean return_value;
306 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
307 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
312 add_scroll_binding (GtkBindingSet *binding_set,
314 GtkScrollType scroll)
316 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
318 gtk_binding_entry_add_signal (binding_set, keyval, 0,
320 GTK_TYPE_SCROLL_TYPE, scroll,
321 G_TYPE_BOOLEAN, FALSE);
322 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
324 GTK_TYPE_SCROLL_TYPE, scroll,
325 G_TYPE_BOOLEAN, FALSE);
329 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
331 GObjectClass *gobject_class;
332 HildonWindowClass *hildon_window_class;
333 ModestWindowClass *modest_window_class;
334 GtkBindingSet *binding_set;
336 gobject_class = (GObjectClass*) klass;
337 hildon_window_class = (HildonWindowClass *) klass;
338 modest_window_class = (ModestWindowClass *) klass;
340 parent_class = g_type_class_peek_parent (klass);
341 gobject_class->finalize = modest_msg_view_window_finalize;
343 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
344 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
345 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
346 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
347 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
348 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
350 modest_window_class->save_state_func = save_state;
352 klass->scroll_child = modest_msg_view_window_scroll_child;
354 signals[MSG_CHANGED_SIGNAL] =
355 g_signal_new ("msg-changed",
356 G_TYPE_FROM_CLASS (gobject_class),
358 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
360 modest_marshal_VOID__POINTER_POINTER,
361 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
363 signals[SCROLL_CHILD_SIGNAL] =
364 g_signal_new ("scroll-child",
365 G_TYPE_FROM_CLASS (gobject_class),
366 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
367 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
369 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
370 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
372 binding_set = gtk_binding_set_by_class (klass);
373 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
374 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
375 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
376 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
377 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
378 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
380 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
384 static void modest_header_view_observer_init(
385 ModestHeaderViewObserverIface *iface_class)
387 iface_class->update_func = modest_msg_view_window_update_model_replaced;
391 modest_msg_view_window_init (ModestMsgViewWindow *obj)
393 ModestMsgViewWindowPrivate *priv;
394 ModestWindowPrivate *parent_priv = NULL;
395 GtkActionGroup *action_group = NULL;
396 GError *error = NULL;
398 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
399 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
400 parent_priv->ui_manager = gtk_ui_manager_new();
402 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
403 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
405 /* Add common actions */
406 gtk_action_group_add_actions (action_group,
407 modest_action_entries,
408 G_N_ELEMENTS (modest_action_entries),
410 gtk_action_group_add_toggle_actions (action_group,
411 msg_view_toggle_action_entries,
412 G_N_ELEMENTS (msg_view_toggle_action_entries),
415 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
416 g_object_unref (action_group);
418 /* Load the UI definition */
419 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
422 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
423 g_error_free (error);
428 /* Add accelerators */
429 gtk_window_add_accel_group (GTK_WINDOW (obj),
430 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
432 priv->is_search_result = FALSE;
433 priv->is_outbox = FALSE;
435 priv->msg_view = NULL;
436 priv->header_model = NULL;
437 priv->header_folder_id = NULL;
438 priv->clipboard_change_handler = 0;
439 priv->queue_change_handler = 0;
440 priv->account_removed_handler = 0;
441 priv->row_changed_handler = 0;
442 priv->row_deleted_handler = 0;
443 priv->row_inserted_handler = 0;
444 priv->rows_reordered_handler = 0;
445 priv->progress_hint = FALSE;
446 priv->fetching_images = 0;
448 priv->optimized_view = FALSE;
449 priv->purge_timeout = 0;
450 priv->remove_attachment_banner = NULL;
451 priv->msg_uid = NULL;
452 priv->other_body = NULL;
454 priv->sighandlers = NULL;
457 init_window (MODEST_MSG_VIEW_WINDOW(obj));
459 hildon_program_add_window (hildon_program_get_instance(),
465 update_progress_hint (ModestMsgViewWindow *self)
467 ModestMsgViewWindowPrivate *priv;
468 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
470 if (GTK_WIDGET_VISIBLE (self)) {
471 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
472 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
477 set_progress_hint (ModestMsgViewWindow *self,
480 ModestWindowPrivate *parent_priv;
481 ModestMsgViewWindowPrivate *priv;
483 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
485 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
486 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
488 /* Sets current progress hint */
489 priv->progress_hint = enabled;
491 update_progress_hint (self);
497 init_window (ModestMsgViewWindow *obj)
499 GtkWidget *main_vbox;
500 ModestMsgViewWindowPrivate *priv;
502 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
504 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
505 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
506 main_vbox = gtk_vbox_new (FALSE, 6);
507 priv->main_scroll = hildon_pannable_area_new ();
508 g_object_set (G_OBJECT (priv->main_scroll),
509 "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
512 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
513 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
514 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
516 /* NULL-ize fields if the window is destroyed */
517 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
519 gtk_widget_show_all (GTK_WIDGET(main_vbox));
523 modest_msg_view_window_disconnect_signals (ModestWindow *self)
525 ModestMsgViewWindowPrivate *priv;
526 GtkWidget *header_view = NULL;
527 GtkWindow *parent_window = NULL;
529 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
531 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
532 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
533 priv->clipboard_change_handler))
534 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
535 priv->clipboard_change_handler);
537 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
538 priv->queue_change_handler))
539 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
540 priv->queue_change_handler);
542 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
543 priv->account_removed_handler))
544 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
545 priv->account_removed_handler);
547 if (priv->header_model) {
548 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
549 priv->row_changed_handler))
550 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
551 priv->row_changed_handler);
553 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
554 priv->row_deleted_handler))
555 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
556 priv->row_deleted_handler);
558 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
559 priv->row_inserted_handler))
560 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
561 priv->row_inserted_handler);
563 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
564 priv->rows_reordered_handler))
565 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
566 priv->rows_reordered_handler);
569 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
570 priv->sighandlers = NULL;
572 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
573 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
574 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
576 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
577 MODEST_HEADER_VIEW_OBSERVER(self));
583 modest_msg_view_window_finalize (GObject *obj)
585 ModestMsgViewWindowPrivate *priv;
587 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
589 /* Sanity check: shouldn't be needed, the window mgr should
590 call this function before */
591 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
593 if (priv->other_body != NULL) {
594 g_object_unref (priv->other_body);
595 priv->other_body = NULL;
598 if (priv->header_model != NULL) {
599 g_object_unref (priv->header_model);
600 priv->header_model = NULL;
603 if (priv->remove_attachment_banner) {
604 gtk_widget_destroy (priv->remove_attachment_banner);
605 g_object_unref (priv->remove_attachment_banner);
606 priv->remove_attachment_banner = NULL;
609 if (priv->purge_timeout > 0) {
610 g_source_remove (priv->purge_timeout);
611 priv->purge_timeout = 0;
614 if (priv->row_reference) {
615 gtk_tree_row_reference_free (priv->row_reference);
616 priv->row_reference = NULL;
619 if (priv->next_row_reference) {
620 gtk_tree_row_reference_free (priv->next_row_reference);
621 priv->next_row_reference = NULL;
625 g_free (priv->msg_uid);
626 priv->msg_uid = NULL;
629 G_OBJECT_CLASS(parent_class)->finalize (obj);
633 select_next_valid_row (GtkTreeModel *model,
634 GtkTreeRowReference **row_reference,
638 GtkTreeIter tmp_iter;
640 GtkTreePath *next = NULL;
641 gboolean retval = FALSE, finished;
643 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
645 path = gtk_tree_row_reference_get_path (*row_reference);
646 gtk_tree_model_get_iter (model, &tmp_iter, path);
647 gtk_tree_row_reference_free (*row_reference);
648 *row_reference = NULL;
652 TnyHeader *header = NULL;
654 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
655 gtk_tree_model_get (model, &tmp_iter,
656 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
660 if (msg_is_visible (header, is_outbox)) {
661 next = gtk_tree_model_get_path (model, &tmp_iter);
662 *row_reference = gtk_tree_row_reference_new (model, next);
663 gtk_tree_path_free (next);
667 g_object_unref (header);
670 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
671 next = gtk_tree_model_get_path (model, &tmp_iter);
673 /* Ensure that we are not selecting the same */
674 if (gtk_tree_path_compare (path, next) != 0) {
675 gtk_tree_model_get (model, &tmp_iter,
676 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
679 if (msg_is_visible (header, is_outbox)) {
680 *row_reference = gtk_tree_row_reference_new (model, next);
684 g_object_unref (header);
688 /* If we ended up in the same message
689 then there is no valid next
693 gtk_tree_path_free (next);
695 /* If there are no more messages and we don't
696 want to start again in the first one then
697 there is no valid next message */
703 gtk_tree_path_free (path);
708 /* TODO: This should be in _init(), with the parameters as properties. */
710 modest_msg_view_window_construct (ModestMsgViewWindow *self,
711 const gchar *modest_account_name,
712 const gchar *mailbox,
713 const gchar *msg_uid)
716 ModestMsgViewWindowPrivate *priv = NULL;
717 ModestWindowPrivate *parent_priv = NULL;
718 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
719 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
721 obj = G_OBJECT (self);
722 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
723 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
725 priv->msg_uid = g_strdup (msg_uid);
728 parent_priv->menubar = NULL;
730 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
731 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
734 /* Add common dimming rules */
735 modest_dimming_rules_group_add_rules (toolbar_rules_group,
736 modest_msg_view_toolbar_dimming_entries,
737 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
738 MODEST_WINDOW (self));
739 modest_dimming_rules_group_add_rules (clipboard_rules_group,
740 modest_msg_view_clipboard_dimming_entries,
741 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
742 MODEST_WINDOW (self));
744 /* Insert dimming rules group for this window */
745 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
746 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
747 g_object_unref (toolbar_rules_group);
748 g_object_unref (clipboard_rules_group);
750 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
752 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);
753 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
754 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
755 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
756 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
757 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
758 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
759 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
760 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
761 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
762 G_CALLBACK (modest_ui_actions_on_details), obj);
763 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
764 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
765 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
766 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
767 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
768 G_CALLBACK (on_fetch_image), obj);
770 g_signal_connect (G_OBJECT (obj), "key-release-event",
771 G_CALLBACK (modest_msg_view_window_key_event),
774 g_signal_connect (G_OBJECT (obj), "key-press-event",
775 G_CALLBACK (modest_msg_view_window_key_event),
778 g_signal_connect (G_OBJECT (obj), "move-focus",
779 G_CALLBACK (on_move_focus), obj);
781 g_signal_connect (G_OBJECT (obj), "map-event",
782 G_CALLBACK (_modest_msg_view_window_map_event),
785 /* Mail Operation Queue */
786 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
788 G_CALLBACK (on_queue_changed),
791 /* Account manager */
792 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
794 G_CALLBACK(on_account_removed),
797 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
798 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
800 /* First add out toolbar ... */
801 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
803 /* ... and later the find toolbar. This way find toolbar will
804 be shown over the other */
805 priv->find_toolbar = hildon_find_toolbar_new (NULL);
806 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
807 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
808 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
809 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
810 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
811 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
812 priv->last_search = NULL;
814 /* Init the clipboard actions dim status */
815 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
817 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
822 /* FIXME: parameter checks */
824 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
825 const gchar *modest_account_name,
826 const gchar *mailbox,
827 const gchar *msg_uid,
829 GtkTreeRowReference *row_reference)
831 ModestMsgViewWindow *window = NULL;
832 ModestMsgViewWindowPrivate *priv = NULL;
833 TnyFolder *header_folder = NULL;
834 ModestHeaderView *header_view = NULL;
835 ModestWindowMgr *mgr = NULL;
838 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
841 mgr = modest_runtime_get_window_mgr ();
842 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
843 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
845 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
847 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
849 /* Remember the message list's TreeModel so we can detect changes
850 * and change the list selection when necessary: */
851 header_folder = modest_header_view_get_folder (header_view);
853 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
854 TNY_FOLDER_TYPE_OUTBOX);
855 priv->header_folder_id = tny_folder_get_id (header_folder);
856 g_object_unref(header_folder);
859 /* Setup row references and connect signals */
860 priv->header_model = g_object_ref (model);
863 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
864 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
865 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
867 priv->row_reference = NULL;
868 priv->next_row_reference = NULL;
871 /* Connect signals */
872 priv->row_changed_handler =
873 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
874 G_CALLBACK(modest_msg_view_window_on_row_changed),
876 priv->row_deleted_handler =
877 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
878 G_CALLBACK(modest_msg_view_window_on_row_deleted),
880 priv->row_inserted_handler =
881 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
882 G_CALLBACK(modest_msg_view_window_on_row_inserted),
884 priv->rows_reordered_handler =
885 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
886 G_CALLBACK(modest_msg_view_window_on_row_reordered),
889 if (header_view != NULL){
890 modest_header_view_add_observer(header_view,
891 MODEST_HEADER_VIEW_OBSERVER(window));
894 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
895 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
896 update_branding (MODEST_MSG_VIEW_WINDOW (window));
898 /* gtk_widget_show_all (GTK_WIDGET (window)); */
899 modest_msg_view_window_update_priority (window);
900 /* Check dimming rules */
901 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
902 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
903 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
905 return MODEST_WINDOW(window);
909 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
910 const gchar *mailbox,
911 const gchar *msg_uid)
913 ModestMsgViewWindow *window = NULL;
914 ModestMsgViewWindowPrivate *priv = NULL;
915 ModestWindowMgr *mgr = NULL;
917 TnyAccount *account = NULL;
919 mgr = modest_runtime_get_window_mgr ();
920 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
921 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
923 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
925 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
929 is_merge = g_str_has_prefix (msg_uid, "merge:");
931 /* Get the account */
933 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
937 if (is_merge || account) {
938 TnyFolder *folder = NULL;
940 /* Try to get the message, if it's already downloaded
941 we don't need to connect */
943 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
945 ModestTnyAccountStore *account_store;
946 ModestTnyLocalFoldersAccount *local_folders_account;
948 account_store = modest_runtime_get_account_store ();
949 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
950 modest_tny_account_store_get_local_folders_account (account_store));
951 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
952 g_object_unref (local_folders_account);
956 gboolean device_online;
958 device = modest_runtime_get_device();
959 device_online = tny_device_is_online (device);
961 message_reader (window, priv, NULL, msg_uid, folder, NULL);
963 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
965 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
966 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
967 update_branding (MODEST_MSG_VIEW_WINDOW (window));
968 g_object_unref (msg);
970 message_reader (window, priv, NULL, msg_uid, folder, NULL);
973 g_object_unref (folder);
978 /* Check dimming rules */
979 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
980 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
981 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
983 return MODEST_WINDOW(window);
987 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
988 const gchar *modest_account_name,
989 const gchar *mailbox,
990 const gchar *msg_uid,
991 GtkTreeRowReference *row_reference)
993 ModestMsgViewWindow *window = NULL;
994 ModestMsgViewWindowPrivate *priv = NULL;
995 TnyFolder *header_folder = NULL;
996 ModestWindowMgr *mgr = NULL;
1000 mgr = modest_runtime_get_window_mgr ();
1001 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1002 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1004 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1006 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1008 /* Remember the message list's TreeModel so we can detect changes
1009 * and change the list selection when necessary: */
1011 if (header_view != NULL){
1012 header_folder = modest_header_view_get_folder(header_view);
1013 /* This could happen if the header folder was
1014 unseleted before opening this msg window (for
1015 example if the user selects an account in the
1016 folder view of the main window */
1017 if (header_folder) {
1018 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1019 TNY_FOLDER_TYPE_OUTBOX);
1020 priv->header_folder_id = tny_folder_get_id(header_folder);
1021 g_object_unref(header_folder);
1025 /* Setup row references and connect signals */
1026 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1027 g_object_ref (priv->header_model);
1029 if (row_reference) {
1030 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1031 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1032 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1034 priv->row_reference = NULL;
1035 priv->next_row_reference = NULL;
1038 /* Connect signals */
1039 priv->row_changed_handler =
1040 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1041 G_CALLBACK(modest_msg_view_window_on_row_changed),
1043 priv->row_deleted_handler =
1044 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1045 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1047 priv->row_inserted_handler =
1048 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1049 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1051 priv->rows_reordered_handler =
1052 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1053 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1056 if (header_view != NULL){
1057 modest_header_view_add_observer(header_view,
1058 MODEST_HEADER_VIEW_OBSERVER(window));
1061 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1062 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1064 path = gtk_tree_row_reference_get_path (row_reference);
1065 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1067 gtk_tree_model_get (priv->header_model, &iter,
1068 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1070 message_reader (window, priv, header, NULL, NULL, row_reference);
1071 g_object_unref (header);
1073 gtk_tree_path_free (path);
1075 /* Check dimming rules */
1076 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1077 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1078 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1080 return MODEST_WINDOW(window);
1084 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1085 const gchar *modest_account_name,
1086 const gchar *mailbox,
1087 const gchar *msg_uid)
1089 ModestMsgViewWindow *window = NULL;
1090 ModestMsgViewWindowPrivate *priv = NULL;
1091 ModestWindowMgr *mgr = NULL;
1093 mgr = modest_runtime_get_window_mgr ();
1094 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1095 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1096 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1098 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1100 /* Remember that this is a search result,
1101 * so we can disable some UI appropriately: */
1102 priv->is_search_result = TRUE;
1104 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1105 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1107 update_window_title (window);
1108 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1109 modest_msg_view_window_update_priority (window);
1111 /* Check dimming rules */
1112 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1113 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1114 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1116 return MODEST_WINDOW(window);
1120 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1122 ModestMsgViewWindowPrivate *priv = NULL;
1124 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1125 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1127 return (priv->other_body != NULL);
1131 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1132 TnyMimePart *other_body,
1133 const gchar *modest_account_name,
1134 const gchar *mailbox,
1135 const gchar *msg_uid)
1137 GObject *obj = NULL;
1138 ModestMsgViewWindowPrivate *priv;
1139 ModestWindowMgr *mgr = NULL;
1141 g_return_val_if_fail (msg, NULL);
1142 mgr = modest_runtime_get_window_mgr ();
1143 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1144 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1145 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1146 modest_account_name, mailbox, msg_uid);
1149 priv->other_body = g_object_ref (other_body);
1150 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1152 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1154 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1155 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1157 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1159 /* Check dimming rules */
1160 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1161 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1162 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1164 return MODEST_WINDOW(obj);
1168 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1169 const gchar *modest_account_name,
1170 const gchar *mailbox,
1171 const gchar *msg_uid)
1173 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1177 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1180 ModestMsgViewWindow *window)
1182 check_dimming_rules_after_change (window);
1186 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1188 ModestMsgViewWindow *window)
1190 check_dimming_rules_after_change (window);
1192 /* The window could have dissapeared */
1195 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1197 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1198 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1202 /* On insertions we check if the folder still has the message we are
1203 * showing or do not. If do not, we do nothing. Which means we are still
1204 * not attached to any header folder and thus next/prev buttons are
1205 * still dimmed. Once the message that is shown by msg-view is found, the
1206 * new model of header-view will be attached and the references will be set.
1207 * On each further insertions dimming rules will be checked. However
1208 * this requires extra CPU time at least works.
1209 * (An message might be deleted from TnyFolder and thus will not be
1210 * inserted into the model again for example if it is removed by the
1211 * imap server and the header view is refreshed.)
1214 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1215 GtkTreePath *tree_path,
1216 GtkTreeIter *tree_iter,
1217 ModestMsgViewWindow *window)
1219 ModestMsgViewWindowPrivate *priv = NULL;
1220 TnyHeader *header = NULL;
1222 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1223 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1225 g_assert (model == priv->header_model);
1227 /* Check if the newly inserted message is the same we are actually
1228 * showing. IF not, we should remain detached from the header model
1229 * and thus prev and next toolbar buttons should remain dimmed. */
1230 gtk_tree_model_get (model, tree_iter,
1231 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1234 if (TNY_IS_HEADER (header)) {
1237 uid = modest_tny_folder_get_header_unique_id (header);
1238 if (!g_str_equal(priv->msg_uid, uid)) {
1239 check_dimming_rules_after_change (window);
1241 g_object_unref (G_OBJECT(header));
1245 g_object_unref(G_OBJECT(header));
1248 if (priv->row_reference) {
1249 gtk_tree_row_reference_free (priv->row_reference);
1252 /* Setup row_reference for the actual msg. */
1253 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1254 if (priv->row_reference == NULL) {
1255 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1259 /* Now set up next_row_reference. */
1260 if (priv->next_row_reference) {
1261 gtk_tree_row_reference_free (priv->next_row_reference);
1264 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1265 select_next_valid_row (priv->header_model,
1266 &(priv->next_row_reference), FALSE, priv->is_outbox);
1268 /* Connect the remaining callbacks to become able to detect
1269 * changes in header-view. */
1270 priv->row_changed_handler =
1271 g_signal_connect (priv->header_model, "row-changed",
1272 G_CALLBACK (modest_msg_view_window_on_row_changed),
1274 priv->row_deleted_handler =
1275 g_signal_connect (priv->header_model, "row-deleted",
1276 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1278 priv->rows_reordered_handler =
1279 g_signal_connect (priv->header_model, "rows-reordered",
1280 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1283 check_dimming_rules_after_change (window);
1287 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1291 ModestMsgViewWindow *window)
1293 ModestMsgViewWindowPrivate *priv = NULL;
1294 gboolean already_changed = FALSE;
1296 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1298 /* If the current row was reordered select the proper next
1299 valid row. The same if the next row reference changes */
1300 if (!priv->row_reference ||
1301 !gtk_tree_row_reference_valid (priv->row_reference))
1304 if (priv->next_row_reference &&
1305 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1306 GtkTreePath *cur, *next;
1307 /* Check that the order is still the correct one */
1308 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1309 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1310 gtk_tree_path_next (cur);
1311 if (gtk_tree_path_compare (cur, next) != 0) {
1312 gtk_tree_row_reference_free (priv->next_row_reference);
1313 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1314 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1315 already_changed = TRUE;
1317 gtk_tree_path_free (cur);
1318 gtk_tree_path_free (next);
1320 if (priv->next_row_reference)
1321 gtk_tree_row_reference_free (priv->next_row_reference);
1322 /* Update next row reference */
1323 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1324 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1325 already_changed = TRUE;
1328 check_dimming_rules_after_change (window);
1331 /* The modest_msg_view_window_update_model_replaced implements update
1332 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1333 * actually belongs to the header-view is the same as the TnyFolder of
1334 * the message of msg-view or not. If they are different, there is
1335 * nothing to do. If they are the same, then the model has replaced and
1336 * the reference in msg-view shall be replaced from the old model to
1337 * the new model. In this case the view will be detached from it's
1338 * header folder. From this point the next/prev buttons are dimmed.
1341 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1342 GtkTreeModel *model,
1343 const gchar *tny_folder_id)
1345 ModestMsgViewWindowPrivate *priv = NULL;
1346 ModestMsgViewWindow *window = NULL;
1348 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1349 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1351 window = MODEST_MSG_VIEW_WINDOW(observer);
1352 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1354 /* If there is an other folder in the header-view then we do
1355 * not care about it's model (msg list). Else if the
1356 * header-view shows the folder the msg shown by us is in, we
1357 * shall replace our model reference and make some check. */
1358 if(model == NULL || tny_folder_id == NULL ||
1359 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1362 /* Model is changed(replaced), so we should forget the old
1363 * one. Because there might be other references and there
1364 * might be some change on the model even if we unreferenced
1365 * it, we need to disconnect our signals here. */
1366 if (priv->header_model) {
1367 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1368 priv->row_changed_handler))
1369 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1370 priv->row_changed_handler);
1371 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1372 priv->row_deleted_handler))
1373 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1374 priv->row_deleted_handler);
1375 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1376 priv->row_inserted_handler))
1377 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1378 priv->row_inserted_handler);
1379 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1380 priv->rows_reordered_handler))
1381 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1382 priv->rows_reordered_handler);
1385 if (priv->row_reference)
1386 gtk_tree_row_reference_free (priv->row_reference);
1387 if (priv->next_row_reference)
1388 gtk_tree_row_reference_free (priv->next_row_reference);
1389 g_object_unref(priv->header_model);
1392 priv->row_changed_handler = 0;
1393 priv->row_deleted_handler = 0;
1394 priv->row_inserted_handler = 0;
1395 priv->rows_reordered_handler = 0;
1396 priv->next_row_reference = NULL;
1397 priv->row_reference = NULL;
1398 priv->header_model = NULL;
1401 priv->header_model = g_object_ref (model);
1403 /* Also we must connect to the new model for row insertions.
1404 * Only for insertions now. We will need other ones only after
1405 * the msg is show by msg-view is added to the new model. */
1406 priv->row_inserted_handler =
1407 g_signal_connect (priv->header_model, "row-inserted",
1408 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1411 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1412 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1416 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1418 ModestMsgViewWindowPrivate *priv= NULL;
1420 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1421 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1423 return priv->progress_hint;
1427 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1429 ModestMsgViewWindowPrivate *priv= NULL;
1431 TnyHeader *header = NULL;
1432 GtkTreePath *path = NULL;
1435 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1436 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1438 /* If the message was not obtained from a treemodel,
1439 * for instance if it was opened directly by the search UI:
1441 if (priv->header_model == NULL ||
1442 priv->row_reference == NULL ||
1443 !gtk_tree_row_reference_valid (priv->row_reference)) {
1444 msg = modest_msg_view_window_get_message (self);
1446 header = tny_msg_get_header (msg);
1447 g_object_unref (msg);
1452 /* Get iter of the currently selected message in the header view: */
1453 path = gtk_tree_row_reference_get_path (priv->row_reference);
1454 g_return_val_if_fail (path != NULL, NULL);
1455 gtk_tree_model_get_iter (priv->header_model,
1459 /* Get current message header */
1460 gtk_tree_model_get (priv->header_model, &iter,
1461 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1464 gtk_tree_path_free (path);
1469 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1471 ModestMsgViewWindowPrivate *priv;
1473 g_return_val_if_fail (self, NULL);
1475 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1477 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1481 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1483 ModestMsgViewWindowPrivate *priv;
1485 g_return_val_if_fail (self, NULL);
1487 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1489 return (const gchar*) priv->msg_uid;
1492 /* Used for the Ctrl+F accelerator */
1494 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1497 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1498 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1500 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1501 modest_msg_view_window_find_toolbar_close (obj, data);
1503 modest_msg_view_window_show_find_toolbar (obj, data);
1507 /* Handler for menu option */
1509 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1512 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1513 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1515 gtk_widget_show (priv->find_toolbar);
1516 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1519 /* Handler for click on the "X" close button in find toolbar */
1521 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1522 ModestMsgViewWindow *obj)
1524 ModestMsgViewWindowPrivate *priv;
1526 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1529 gtk_widget_hide (priv->find_toolbar);
1530 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1534 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1535 ModestMsgViewWindow *obj)
1537 gchar *current_search;
1538 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1540 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1541 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1545 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1547 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1548 g_free (current_search);
1549 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1553 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1555 g_free (priv->last_search);
1556 priv->last_search = g_strdup (current_search);
1557 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1560 hildon_banner_show_information (NULL, NULL,
1561 _HL("ckct_ib_find_no_matches"));
1562 g_free (priv->last_search);
1563 priv->last_search = NULL;
1565 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1568 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1569 hildon_banner_show_information (NULL, NULL,
1570 _HL("ckct_ib_find_search_complete"));
1571 g_free (priv->last_search);
1572 priv->last_search = NULL;
1574 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1578 g_free (current_search);
1583 modest_msg_view_window_set_zoom (ModestWindow *window,
1586 ModestMsgViewWindowPrivate *priv;
1587 ModestWindowPrivate *parent_priv;
1589 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1591 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1592 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1593 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1598 modest_msg_view_window_get_zoom (ModestWindow *window)
1600 ModestMsgViewWindowPrivate *priv;
1602 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1604 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1605 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1609 modest_msg_view_window_zoom_plus (ModestWindow *window)
1612 ModestMsgViewWindowPrivate *priv;
1616 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1617 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1619 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1621 if (zoom_level >= 2.0) {
1622 hildon_banner_show_information (NULL, NULL,
1623 _CS("ckct_ib_max_zoom_level_reached"));
1625 } else if (zoom_level >= 1.5) {
1627 } else if (zoom_level >= 1.2) {
1629 } else if (zoom_level >= 1.0) {
1631 } else if (zoom_level >= 0.8) {
1633 } else if (zoom_level >= 0.5) {
1639 /* set zoom level */
1640 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1641 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1642 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1643 g_free (banner_text);
1644 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1650 modest_msg_view_window_zoom_minus (ModestWindow *window)
1653 ModestMsgViewWindowPrivate *priv;
1657 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1658 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1660 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1662 if (zoom_level <= 0.5) {
1663 hildon_banner_show_information (NULL, NULL,
1664 _CS("ckct_ib_min_zoom_level_reached"));
1666 } else if (zoom_level <= 0.8) {
1668 } else if (zoom_level <= 1.0) {
1670 } else if (zoom_level <= 1.2) {
1672 } else if (zoom_level <= 1.5) {
1674 } else if (zoom_level <= 2.0) {
1680 /* set zoom level */
1681 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1682 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1683 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1684 g_free (banner_text);
1685 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1692 modest_msg_view_window_key_event (GtkWidget *window,
1698 focus = gtk_window_get_focus (GTK_WINDOW (window));
1700 /* for the find toolbar case */
1701 if (focus && GTK_IS_ENTRY (focus)) {
1702 if (event->keyval == GDK_BackSpace) {
1704 copy = gdk_event_copy ((GdkEvent *) event);
1705 gtk_widget_event (focus, copy);
1706 gdk_event_free (copy);
1711 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1712 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1713 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1714 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1715 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1716 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1717 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1718 /* gboolean return_value; */
1720 if (event->type == GDK_KEY_PRESS) {
1721 GtkScrollType scroll_type;
1723 switch (event->keyval) {
1726 scroll_type = GTK_SCROLL_STEP_UP; break;
1729 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1731 case GDK_KP_Page_Up:
1732 scroll_type = GTK_SCROLL_PAGE_UP; break;
1734 case GDK_KP_Page_Down:
1735 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1738 scroll_type = GTK_SCROLL_START; break;
1741 scroll_type = GTK_SCROLL_END; break;
1742 default: scroll_type = GTK_SCROLL_NONE;
1745 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1746 /* scroll_type, FALSE, &return_value); */
1757 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1760 ModestMsgViewWindowPrivate *priv;
1761 GtkTreeIter tmp_iter;
1762 gboolean is_last_selected;
1764 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1765 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1767 /*if no model (so no rows at all), then virtually we are the last*/
1768 if (!priv->header_model || !priv->row_reference)
1771 if (!gtk_tree_row_reference_valid (priv->row_reference))
1774 path = gtk_tree_row_reference_get_path (priv->row_reference);
1778 is_last_selected = TRUE;
1779 while (is_last_selected) {
1781 gtk_tree_path_next (path);
1782 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1784 gtk_tree_model_get (priv->header_model, &tmp_iter,
1785 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1788 if (msg_is_visible (header, priv->is_outbox))
1789 is_last_selected = FALSE;
1790 g_object_unref(G_OBJECT(header));
1793 gtk_tree_path_free (path);
1794 return is_last_selected;
1798 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1800 ModestMsgViewWindowPrivate *priv;
1802 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1803 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1805 return priv->header_model != NULL;
1809 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1811 ModestMsgViewWindowPrivate *priv;
1813 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1814 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1816 return priv->is_search_result;
1820 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1822 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1824 if (!check_outbox) {
1827 ModestTnySendQueueStatus status;
1828 status = modest_tny_all_send_queues_get_msg_status (header);
1829 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1830 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1835 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1838 ModestMsgViewWindowPrivate *priv;
1839 gboolean is_first_selected;
1840 GtkTreeIter tmp_iter;
1842 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1843 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1845 /*if no model (so no rows at all), then virtually we are the first*/
1846 if (!priv->header_model || !priv->row_reference)
1849 if (!gtk_tree_row_reference_valid (priv->row_reference))
1852 path = gtk_tree_row_reference_get_path (priv->row_reference);
1856 is_first_selected = TRUE;
1857 while (is_first_selected) {
1859 if(!gtk_tree_path_prev (path))
1861 /* Here the 'if' is needless for logic, but let make sure
1862 * iter is valid for gtk_tree_model_get. */
1863 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1865 gtk_tree_model_get (priv->header_model, &tmp_iter,
1866 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1869 if (msg_is_visible (header, priv->is_outbox))
1870 is_first_selected = FALSE;
1871 g_object_unref(G_OBJECT(header));
1874 gtk_tree_path_free (path);
1875 return is_first_selected;
1882 GtkTreeRowReference *row_reference;
1886 message_reader_performer (gboolean canceled,
1888 GtkWindow *parent_window,
1889 TnyAccount *account,
1892 ModestMailOperation *mail_op = NULL;
1893 MsgReaderInfo *info;
1895 info = (MsgReaderInfo *) user_data;
1896 if (canceled || err) {
1897 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1901 /* Register the header - it'll be unregistered in the callback */
1903 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1905 /* New mail operation */
1906 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1907 modest_ui_actions_disk_operations_error_handler,
1910 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1912 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1914 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1915 g_object_unref (mail_op);
1917 /* Update dimming rules */
1918 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1919 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1922 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1923 g_free (info->msg_uid);
1925 g_object_unref (info->folder);
1927 g_object_unref (info->header);
1928 g_slice_free (MsgReaderInfo, info);
1933 * Reads the message whose summary item is @header. It takes care of
1934 * several things, among others:
1936 * If the message was not previously downloaded then ask the user
1937 * before downloading. If there is no connection launch the connection
1938 * dialog. Update toolbar dimming rules.
1940 * Returns: TRUE if the mail operation was started, otherwise if the
1941 * user do not want to download the message, or if the user do not
1942 * want to connect, then the operation is not issued
1945 message_reader (ModestMsgViewWindow *window,
1946 ModestMsgViewWindowPrivate *priv,
1948 const gchar *msg_uid,
1950 GtkTreeRowReference *row_reference)
1952 ModestWindowMgr *mgr;
1953 TnyAccount *account = NULL;
1954 MsgReaderInfo *info;
1956 /* We set the header from model while we're loading */
1957 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1958 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1964 g_object_ref (folder);
1966 mgr = modest_runtime_get_window_mgr ();
1967 /* Msg download completed */
1968 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1970 /* Ask the user if he wants to download the message if
1972 if (!tny_device_is_online (modest_runtime_get_device())) {
1973 GtkResponseType response;
1975 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1976 _("mcen_nc_get_msg"));
1977 if (response == GTK_RESPONSE_CANCEL) {
1978 update_window_title (window);
1983 folder = tny_header_get_folder (header);
1985 info = g_slice_new (MsgReaderInfo);
1986 info->msg_uid = g_strdup (msg_uid);
1988 info->header = g_object_ref (header);
1990 info->header = NULL;
1992 info->folder = g_object_ref (folder);
1994 info->folder = NULL;
1995 if (row_reference) {
1996 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1998 info->row_reference = NULL;
2001 /* Offer the connection dialog if necessary */
2002 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2004 TNY_FOLDER_STORE (folder),
2005 message_reader_performer,
2008 g_object_unref (folder);
2014 folder = tny_header_get_folder (header);
2017 account = tny_folder_get_account (folder);
2019 info = g_slice_new (MsgReaderInfo);
2020 info->msg_uid = g_strdup (msg_uid);
2022 info->folder = g_object_ref (folder);
2024 info->folder = NULL;
2026 info->header = g_object_ref (header);
2028 info->header = NULL;
2030 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2032 info->row_reference = NULL;
2034 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2036 g_object_unref (account);
2038 g_object_unref (folder);
2044 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2046 ModestMsgViewWindowPrivate *priv;
2047 GtkTreePath *path= NULL;
2048 GtkTreeIter tmp_iter;
2050 gboolean retval = TRUE;
2051 GtkTreeRowReference *row_reference = NULL;
2053 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2054 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2056 if (!priv->row_reference)
2059 /* Update the next row reference if it's not valid. This could
2060 happen if for example the header which it was pointing to,
2061 was deleted. The best place to do it is in the row-deleted
2062 handler but the tinymail model do not work like the glib
2063 tree models and reports the deletion when the row is still
2065 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2066 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2067 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2068 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2071 if (priv->next_row_reference)
2072 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2076 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2078 gtk_tree_model_get_iter (priv->header_model,
2081 gtk_tree_path_free (path);
2083 gtk_tree_model_get (priv->header_model, &tmp_iter,
2084 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2087 /* Read the message & show it */
2088 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2091 gtk_tree_row_reference_free (row_reference);
2094 g_object_unref (header);
2100 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2102 ModestMsgViewWindowPrivate *priv = NULL;
2104 gboolean finished = FALSE;
2105 gboolean retval = FALSE;
2107 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2108 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2110 /* Return inmediatly if there is no header model */
2111 if (!priv->header_model || !priv->row_reference)
2114 path = gtk_tree_row_reference_get_path (priv->row_reference);
2115 while (!finished && gtk_tree_path_prev (path)) {
2119 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2120 gtk_tree_model_get (priv->header_model, &iter,
2121 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2125 if (msg_is_visible (header, priv->is_outbox)) {
2126 GtkTreeRowReference *row_reference;
2127 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2128 /* Read the message & show it */
2129 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2130 gtk_tree_row_reference_free (row_reference);
2134 g_object_unref (header);
2138 gtk_tree_path_free (path);
2143 view_msg_cb (ModestMailOperation *mail_op,
2150 ModestMsgViewWindow *self = NULL;
2151 ModestMsgViewWindowPrivate *priv = NULL;
2152 GtkTreeRowReference *row_reference = NULL;
2154 /* Unregister the header (it was registered before creating the mail operation) */
2155 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2157 row_reference = (GtkTreeRowReference *) user_data;
2160 gtk_tree_row_reference_free (row_reference);
2161 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2163 /* Restore window title */
2164 update_window_title (self);
2165 g_object_unref (self);
2170 /* If there was any error */
2171 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2173 gtk_tree_row_reference_free (row_reference);
2174 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2176 /* Restore window title */
2177 update_window_title (self);
2178 g_object_unref (self);
2183 /* Get the window */
2184 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2185 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2186 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2188 /* Update the row reference */
2189 if (priv->row_reference != NULL) {
2190 gtk_tree_row_reference_free (priv->row_reference);
2191 priv->row_reference = row_reference?gtk_tree_row_reference_copy (row_reference):NULL;
2192 if (priv->next_row_reference != NULL) {
2193 gtk_tree_row_reference_free (priv->next_row_reference);
2195 if (row_reference) {
2196 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2197 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2199 priv->next_row_reference = NULL;
2203 /* Mark header as read */
2204 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2205 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2207 /* Set new message */
2208 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2209 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2210 modest_msg_view_window_update_priority (self);
2211 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2212 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2213 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2216 /* Set the new message uid of the window */
2217 if (priv->msg_uid) {
2218 g_free (priv->msg_uid);
2219 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2222 /* Notify the observers */
2223 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2224 0, priv->header_model, priv->row_reference);
2227 g_object_unref (self);
2229 gtk_tree_row_reference_free (row_reference);
2233 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2235 ModestMsgViewWindowPrivate *priv;
2237 TnyFolderType folder_type;
2239 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2241 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2243 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2247 folder = tny_msg_get_folder (msg);
2249 folder_type = modest_tny_folder_guess_folder_type (folder);
2250 g_object_unref (folder);
2252 g_object_unref (msg);
2260 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2262 ModestMsgViewWindowPrivate *priv;
2263 TnyHeader *header = NULL;
2264 TnyHeaderFlags flags = 0;
2266 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2268 if (priv->header_model && priv->row_reference) {
2270 GtkTreePath *path = NULL;
2272 path = gtk_tree_row_reference_get_path (priv->row_reference);
2273 g_return_if_fail (path != NULL);
2274 gtk_tree_model_get_iter (priv->header_model,
2276 gtk_tree_row_reference_get_path (priv->row_reference));
2278 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2280 gtk_tree_path_free (path);
2283 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2285 header = tny_msg_get_header (msg);
2286 g_object_unref (msg);
2291 flags = tny_header_get_flags (header);
2292 g_object_unref(G_OBJECT(header));
2295 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2300 toolbar_resize (ModestMsgViewWindow *self)
2302 ModestMsgViewWindowPrivate *priv = NULL;
2303 ModestWindowPrivate *parent_priv = NULL;
2305 gint static_button_size;
2306 ModestWindowMgr *mgr;
2308 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2309 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2310 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2312 mgr = modest_runtime_get_window_mgr ();
2313 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2315 if (parent_priv->toolbar) {
2316 /* left size buttons */
2317 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2318 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2319 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2320 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2321 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2322 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2323 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2324 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2325 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2326 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2327 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2328 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2329 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2330 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2331 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2332 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2334 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2335 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2336 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2337 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2342 modest_msg_view_window_show_toolbar (ModestWindow *self,
2343 gboolean show_toolbar)
2345 ModestMsgViewWindowPrivate *priv = NULL;
2346 ModestWindowPrivate *parent_priv;
2348 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2349 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2351 /* Set optimized view status */
2352 priv->optimized_view = !show_toolbar;
2354 if (!parent_priv->toolbar) {
2355 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2357 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2358 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2360 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2361 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2362 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2365 hildon_window_add_toolbar (HILDON_WINDOW (self),
2366 GTK_TOOLBAR (parent_priv->toolbar));
2371 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2372 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2373 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2375 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2376 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2377 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2379 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2382 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2383 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2388 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2390 ModestMsgViewWindow *window)
2392 if (!GTK_WIDGET_VISIBLE (window))
2395 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2399 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2401 ModestMsgViewWindowPrivate *priv;
2403 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2404 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2406 return priv->progress_hint;
2410 observers_empty (ModestMsgViewWindow *self)
2413 ModestMsgViewWindowPrivate *priv;
2414 gboolean is_empty = TRUE;
2415 guint pending_ops = 0;
2417 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2418 tmp = priv->progress_widgets;
2420 /* Check all observers */
2421 while (tmp && is_empty) {
2422 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2423 is_empty = pending_ops == 0;
2425 tmp = g_slist_next(tmp);
2432 on_account_removed (TnyAccountStore *account_store,
2433 TnyAccount *account,
2436 /* Do nothing if it's a transport account, because we only
2437 show the messages of a store account */
2438 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2439 const gchar *parent_acc = NULL;
2440 const gchar *our_acc = NULL;
2442 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2443 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2445 /* Close this window if I'm showing a message of the removed account */
2446 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2447 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2452 on_mail_operation_started (ModestMailOperation *mail_op,
2455 ModestMsgViewWindow *self;
2456 ModestMailOperationTypeOperation op_type;
2458 ModestMsgViewWindowPrivate *priv;
2459 GObject *source = NULL;
2461 self = MODEST_MSG_VIEW_WINDOW (user_data);
2462 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2463 op_type = modest_mail_operation_get_type_operation (mail_op);
2464 tmp = priv->progress_widgets;
2465 source = modest_mail_operation_get_source(mail_op);
2466 if (G_OBJECT (self) == source) {
2467 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2468 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2469 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2470 set_progress_hint (self, TRUE);
2472 modest_progress_object_add_operation (
2473 MODEST_PROGRESS_OBJECT (tmp->data),
2475 tmp = g_slist_next (tmp);
2479 g_object_unref (source);
2481 /* Update dimming rules */
2482 check_dimming_rules_after_change (self);
2486 on_mail_operation_finished (ModestMailOperation *mail_op,
2489 ModestMsgViewWindow *self;
2490 ModestMailOperationTypeOperation op_type;
2492 ModestMsgViewWindowPrivate *priv;
2494 self = MODEST_MSG_VIEW_WINDOW (user_data);
2495 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2496 op_type = modest_mail_operation_get_type_operation (mail_op);
2497 tmp = priv->progress_widgets;
2499 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2500 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2501 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2503 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2505 tmp = g_slist_next (tmp);
2508 /* If no more operations are being observed, NORMAL mode is enabled again */
2509 if (observers_empty (self)) {
2510 set_progress_hint (self, FALSE);
2514 /* Update dimming rules. We have to do this right here
2515 and not in view_msg_cb because at that point the
2516 transfer mode is still enabled so the dimming rule
2517 won't let the user delete the message that has been
2518 readed for example */
2519 check_dimming_rules_after_change (self);
2523 on_queue_changed (ModestMailOperationQueue *queue,
2524 ModestMailOperation *mail_op,
2525 ModestMailOperationQueueNotification type,
2526 ModestMsgViewWindow *self)
2528 ModestMsgViewWindowPrivate *priv;
2530 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2532 /* If this operations was created by another window, do nothing */
2533 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2536 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2537 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2539 "operation-started",
2540 G_CALLBACK (on_mail_operation_started),
2542 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2544 "operation-finished",
2545 G_CALLBACK (on_mail_operation_finished),
2547 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2548 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2550 "operation-started");
2551 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2553 "operation-finished");
2558 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2560 ModestMsgViewWindowPrivate *priv;
2561 TnyList *selected_attachments = NULL;
2563 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2564 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2566 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2567 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2569 return selected_attachments;
2573 ModestMsgViewWindow *self;
2575 } DecodeAsyncHelper;
2578 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2584 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2586 /* It could happen that the window was closed */
2587 if (GTK_WIDGET_VISIBLE (helper->self))
2588 set_progress_hint (helper->self, FALSE);
2590 if (cancelled || err) {
2592 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2593 modest_platform_information_banner (NULL, NULL, msg);
2599 /* make the file read-only */
2600 g_chmod(helper->file_path, 0444);
2602 /* Activate the file */
2603 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2607 g_object_unref (helper->self);
2608 g_free (helper->file_path);
2609 g_slice_free (DecodeAsyncHelper, helper);
2613 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2614 TnyMimePart *mime_part)
2616 ModestMsgViewWindowPrivate *priv;
2617 const gchar *msg_uid;
2618 gchar *attachment_uid = NULL;
2619 gint attachment_index = 0;
2620 TnyList *attachments;
2622 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2623 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2624 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2626 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2627 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2628 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2629 g_object_unref (attachments);
2631 if (msg_uid && attachment_index >= 0) {
2632 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2635 if (mime_part == NULL) {
2636 gboolean error = FALSE;
2637 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2638 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2640 } else if (tny_list_get_length (selected_attachments) > 1) {
2641 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2645 iter = tny_list_create_iterator (selected_attachments);
2646 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2647 g_object_unref (iter);
2649 if (selected_attachments)
2650 g_object_unref (selected_attachments);
2655 g_object_ref (mime_part);
2658 if (tny_mime_part_is_purged (mime_part))
2661 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2662 gchar *filepath = NULL;
2663 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2664 gboolean show_error_banner = FALSE;
2665 TnyFsStream *temp_stream = NULL;
2666 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2669 if (temp_stream != NULL) {
2670 DecodeAsyncHelper *helper;
2672 /* Activate progress hint */
2673 set_progress_hint (window, TRUE);
2675 helper = g_slice_new0 (DecodeAsyncHelper);
2676 helper->self = g_object_ref (window);
2677 helper->file_path = g_strdup (filepath);
2679 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2680 on_decode_to_stream_async_handler,
2683 g_object_unref (temp_stream);
2684 /* NOTE: files in the temporary area will be automatically
2685 * cleaned after some time if they are no longer in use */
2688 const gchar *content_type;
2689 /* the file may already exist but it isn't writable,
2690 * let's try to open it anyway */
2691 content_type = tny_mime_part_get_content_type (mime_part);
2692 modest_platform_activate_file (filepath, content_type);
2694 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2695 show_error_banner = TRUE;
2700 if (show_error_banner)
2701 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2702 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2703 ModestWindowMgr *mgr;
2704 ModestWindow *msg_win = NULL;
2705 TnyMsg *current_msg;
2709 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2710 mgr = modest_runtime_get_window_mgr ();
2711 header = tny_msg_get_header (TNY_MSG (current_msg));
2712 found = modest_window_mgr_find_registered_message_uid (mgr,
2717 g_debug ("window for this body is already being created");
2720 /* it's not found, so create a new window for it */
2721 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2722 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2723 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2725 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2727 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2728 account, mailbox, attachment_uid);
2730 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2731 modest_window_get_zoom (MODEST_WINDOW (window)));
2732 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2733 gtk_widget_show_all (GTK_WIDGET (msg_win));
2735 gtk_widget_destroy (GTK_WIDGET (msg_win));
2737 g_object_unref (current_msg);
2739 /* message attachment */
2740 TnyHeader *header = NULL;
2741 ModestWindowMgr *mgr;
2742 ModestWindow *msg_win = NULL;
2745 header = tny_msg_get_header (TNY_MSG (mime_part));
2746 mgr = modest_runtime_get_window_mgr ();
2747 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2750 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2751 * thus, we don't do anything */
2752 g_debug ("window for is already being created");
2754 /* it's not found, so create a new window for it */
2755 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2756 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2757 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2759 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2760 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2761 mailbox, attachment_uid);
2762 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2763 modest_window_get_zoom (MODEST_WINDOW (window)));
2764 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2765 gtk_widget_show_all (GTK_WIDGET (msg_win));
2767 gtk_widget_destroy (GTK_WIDGET (msg_win));
2773 g_free (attachment_uid);
2775 g_object_unref (mime_part);
2787 GnomeVFSResult result;
2791 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2792 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2793 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2794 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2797 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2801 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2802 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2803 g_free (pair->filename);
2804 g_object_unref (pair->part);
2805 g_slice_free (SaveMimePartPair, pair);
2807 g_list_free (info->pairs);
2811 g_slice_free (SaveMimePartInfo, info);
2816 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2818 /* This is a GDK lock because we are an idle callback and
2819 * hildon_banner_show_information is or does Gtk+ code */
2821 gdk_threads_enter (); /* CHECKED */
2822 if (info->result == GNOME_VFS_OK) {
2823 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2824 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2827 /* Check if the uri belongs to the external mmc */
2828 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2829 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2831 msg = g_strdup (_KR("cerm_memory_card_full"));
2832 modest_platform_information_banner (NULL, NULL, msg);
2835 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2837 save_mime_part_info_free (info, FALSE);
2838 gdk_threads_leave (); /* CHECKED */
2844 save_mime_part_to_file (SaveMimePartInfo *info)
2846 GnomeVFSHandle *handle;
2848 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2850 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2851 if (info->result == GNOME_VFS_OK) {
2852 GError *error = NULL;
2853 stream = tny_vfs_stream_new (handle);
2854 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2855 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2857 if ((error->domain == TNY_ERROR_DOMAIN) &&
2858 (error->code == TNY_IO_ERROR_WRITE) &&
2859 (errno == ENOSPC)) {
2860 info->result = GNOME_VFS_ERROR_NO_SPACE;
2862 info->result = GNOME_VFS_ERROR_IO;
2865 g_object_unref (G_OBJECT (stream));
2867 g_warning ("Could not create save attachment %s: %s\n",
2868 pair->filename, gnome_vfs_result_to_string (info->result));
2871 /* Go on saving remaining files */
2872 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2873 if (info->pairs != NULL) {
2874 save_mime_part_to_file (info);
2876 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2883 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2884 SaveMimePartInfo *info)
2886 gboolean is_ok = TRUE;
2887 gint replaced_files = 0;
2888 const GList *files = info->pairs;
2889 const GList *iter, *to_replace = NULL;
2891 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2892 SaveMimePartPair *pair = iter->data;
2893 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
2895 if (modest_utils_file_exists (unescaped)) {
2897 if (replaced_files == 1)
2902 if (replaced_files) {
2905 if (replaced_files == 1) {
2906 SaveMimePartPair *pair = to_replace->data;
2907 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2908 gchar *escaped_basename, *message;
2910 escaped_basename = g_uri_unescape_string (basename, NULL);
2911 message = g_strdup_printf ("%s\n%s",
2912 _FM("docm_nc_replace_file"),
2913 (escaped_basename) ? escaped_basename : "");
2914 response = modest_platform_run_confirmation_dialog (parent, message);
2916 g_free (escaped_basename);
2918 response = modest_platform_run_confirmation_dialog (parent,
2919 _FM("docm_nc_replace_multiple"));
2921 if (response != GTK_RESPONSE_OK)
2926 save_mime_part_info_free (info, TRUE);
2928 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2934 save_attachments_response (GtkDialog *dialog,
2938 TnyList *mime_parts;
2940 GList *files_to_save = NULL;
2941 gchar *current_folder;
2943 mime_parts = TNY_LIST (user_data);
2945 if (arg1 != GTK_RESPONSE_OK)
2948 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2949 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2950 if (current_folder && *current_folder != '\0') {
2952 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2953 current_folder,&err);
2955 g_debug ("Error storing latest used folder: %s", err->message);
2959 g_free (current_folder);
2961 if (!modest_utils_folder_writable (chooser_uri)) {
2962 const gchar *err_msg;
2964 #ifdef MODEST_PLATFORM_MAEMO
2965 if (modest_maemo_utils_in_usb_mode ()) {
2966 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
2968 err_msg = _FM("sfil_ib_readonly_location");
2971 err_msg = _FM("sfil_ib_readonly_location");
2973 hildon_banner_show_information (NULL, NULL, err_msg);
2977 iter = tny_list_create_iterator (mime_parts);
2978 while (!tny_iterator_is_done (iter)) {
2979 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2981 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2982 !tny_mime_part_is_purged (mime_part) &&
2983 (tny_mime_part_get_filename (mime_part) != NULL)) {
2984 SaveMimePartPair *pair;
2986 pair = g_slice_new0 (SaveMimePartPair);
2988 if (tny_list_get_length (mime_parts) > 1) {
2990 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2991 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2994 pair->filename = g_strdup (chooser_uri);
2996 pair->part = mime_part;
2997 files_to_save = g_list_prepend (files_to_save, pair);
2999 tny_iterator_next (iter);
3001 g_object_unref (iter);
3004 if (files_to_save != NULL) {
3005 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3006 info->pairs = files_to_save;
3007 info->result = TRUE;
3008 info->uri = g_strdup (chooser_uri);
3009 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3011 g_free (chooser_uri);
3014 /* Free and close the dialog */
3015 g_object_unref (mime_parts);
3016 gtk_widget_destroy (GTK_WIDGET (dialog));
3020 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3021 TnyList *mime_parts)
3023 ModestMsgViewWindowPrivate *priv;
3024 GtkWidget *save_dialog = NULL;
3025 gchar *conf_folder = NULL;
3026 gchar *filename = NULL;
3027 gchar *save_multiple_str = NULL;
3028 const gchar *root_folder = "file:///";
3030 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3031 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3033 if (mime_parts == NULL) {
3034 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3035 * selection available */
3036 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3037 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3038 g_object_unref (mime_parts);
3041 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3043 g_object_unref (mime_parts);
3049 g_object_ref (mime_parts);
3052 /* prepare dialog */
3053 if (tny_list_get_length (mime_parts) == 1) {
3055 /* only one attachment selected */
3056 iter = tny_list_create_iterator (mime_parts);
3057 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3058 g_object_unref (iter);
3059 if (!modest_tny_mime_part_is_msg (mime_part) &&
3060 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3061 !tny_mime_part_is_purged (mime_part)) {
3062 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3064 /* TODO: show any error? */
3065 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3066 g_object_unref (mime_parts);
3069 g_object_unref (mime_part);
3071 gint num = tny_list_get_length (mime_parts);
3072 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3073 "sfil_va_number_of_objects_attachment",
3074 "sfil_va_number_of_objects_attachments",
3078 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3079 GTK_FILE_CHOOSER_ACTION_SAVE);
3081 /* Get last used folder */
3082 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3083 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3085 /* File chooser stops working if we select "file:///" as current folder */
3086 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3087 g_free (conf_folder);
3091 if (conf_folder && conf_folder[0] != '\0') {
3092 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3095 /* Set the default folder to documents folder */
3096 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3099 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3101 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3102 g_free (docs_folder);
3104 g_free (conf_folder);
3108 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3113 /* if multiple, set multiple string */
3114 if (save_multiple_str) {
3115 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3116 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3117 g_free (save_multiple_str);
3120 /* We must run this asynchronously, because the hildon dialog
3121 performs a gtk_dialog_run by itself which leads to gdk
3123 g_signal_connect (save_dialog, "response",
3124 G_CALLBACK (save_attachments_response), mime_parts);
3126 gtk_widget_show_all (save_dialog);
3130 show_remove_attachment_information (gpointer userdata)
3132 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3133 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3135 /* We're outside the main lock */
3136 gdk_threads_enter ();
3138 if (priv->remove_attachment_banner != NULL) {
3139 gtk_widget_destroy (priv->remove_attachment_banner);
3140 g_object_unref (priv->remove_attachment_banner);
3143 priv->remove_attachment_banner = g_object_ref (
3144 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3146 gdk_threads_leave ();
3152 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3154 ModestMsgViewWindowPrivate *priv;
3155 TnyList *mime_parts = NULL, *tmp;
3156 gchar *confirmation_message;
3162 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3163 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3165 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3166 * because we don't have selection
3168 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3170 /* Remove already purged messages from mime parts list. We use
3171 a copy of the list to remove items in the original one */
3172 tmp = tny_list_copy (mime_parts);
3173 iter = tny_list_create_iterator (tmp);
3174 while (!tny_iterator_is_done (iter)) {
3175 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3176 if (tny_mime_part_is_purged (part))
3177 tny_list_remove (mime_parts, (GObject *) part);
3179 g_object_unref (part);
3180 tny_iterator_next (iter);
3182 g_object_unref (tmp);
3183 g_object_unref (iter);
3185 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3186 tny_list_get_length (mime_parts) == 0) {
3187 g_object_unref (mime_parts);
3191 n_attachments = tny_list_get_length (mime_parts);
3192 if (n_attachments == 1) {
3196 iter = tny_list_create_iterator (mime_parts);
3197 part = (TnyMimePart *) tny_iterator_get_current (iter);
3198 g_object_unref (iter);
3199 if (modest_tny_mime_part_is_msg (part)) {
3201 header = tny_msg_get_header (TNY_MSG (part));
3202 filename = tny_header_dup_subject (header);
3203 g_object_unref (header);
3204 if (filename == NULL)
3205 filename = g_strdup (_("mail_va_no_subject"));
3207 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3209 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3211 g_object_unref (part);
3213 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3214 "mcen_nc_purge_files_text",
3215 n_attachments), n_attachments);
3217 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3218 confirmation_message);
3219 g_free (confirmation_message);
3221 if (response != GTK_RESPONSE_OK) {
3222 g_object_unref (mime_parts);
3226 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3228 iter = tny_list_create_iterator (mime_parts);
3229 while (!tny_iterator_is_done (iter)) {
3232 part = (TnyMimePart *) tny_iterator_get_current (iter);
3233 tny_mime_part_set_purged (TNY_MIME_PART (part));
3234 g_object_unref (part);
3235 tny_iterator_next (iter);
3237 g_object_unref (iter);
3239 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3240 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3241 tny_msg_rewrite_cache (msg);
3242 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3243 g_object_unref (msg);
3244 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3246 g_object_unref (mime_parts);
3248 if (priv->purge_timeout > 0) {
3249 g_source_remove (priv->purge_timeout);
3250 priv->purge_timeout = 0;
3253 if (priv->remove_attachment_banner) {
3254 gtk_widget_destroy (priv->remove_attachment_banner);
3255 g_object_unref (priv->remove_attachment_banner);
3256 priv->remove_attachment_banner = NULL;
3262 update_window_title (ModestMsgViewWindow *window)
3264 ModestMsgViewWindowPrivate *priv;
3266 TnyHeader *header = NULL;
3267 gchar *subject = NULL;
3269 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3271 /* Note that if the window is closed while we're retrieving
3272 the message, this widget could de deleted */
3273 if (!priv->msg_view)
3276 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3278 if (priv->other_body) {
3281 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3283 g_strstrip (description);
3284 subject = description;
3286 } else if (msg != NULL) {
3287 header = tny_msg_get_header (msg);
3288 subject = tny_header_dup_subject (header);
3289 g_object_unref (header);
3290 g_object_unref (msg);
3293 if ((subject == NULL)||(subject[0] == '\0')) {
3295 subject = g_strdup (_("mail_va_no_subject"));
3298 gtk_window_set_title (GTK_WINDOW (window), subject);
3303 on_move_focus (GtkWidget *widget,
3304 GtkDirectionType direction,
3307 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3311 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3313 GnomeVFSResult result;
3314 GnomeVFSHandle *handle = NULL;
3315 GnomeVFSFileInfo *info = NULL;
3318 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3319 if (result != GNOME_VFS_OK) {
3324 info = gnome_vfs_file_info_new ();
3325 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3326 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3327 /* We put a "safe" default size for going to cache */
3328 *expected_size = (300*1024);
3330 *expected_size = info->size;
3332 gnome_vfs_file_info_unref (info);
3334 stream = tny_vfs_stream_new (handle);
3343 TnyStream *output_stream;
3344 GtkWidget *msg_view;
3349 on_fetch_image_idle_refresh_view (gpointer userdata)
3352 FetchImageData *fidata = (FetchImageData *) userdata;
3354 gdk_threads_enter ();
3355 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3356 ModestMsgViewWindowPrivate *priv;
3358 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3359 priv->fetching_images--;
3360 gtk_widget_queue_draw (fidata->msg_view);
3361 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3363 gdk_threads_leave ();
3365 g_object_unref (fidata->msg_view);
3366 g_object_unref (fidata->window);
3367 g_slice_free (FetchImageData, fidata);
3372 on_fetch_image_thread (gpointer userdata)
3374 FetchImageData *fidata = (FetchImageData *) userdata;
3375 TnyStreamCache *cache;
3376 TnyStream *cache_stream;
3378 cache = modest_runtime_get_images_cache ();
3380 tny_stream_cache_get_stream (cache,
3382 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3383 (gpointer) fidata->uri);
3384 g_free (fidata->cache_id);
3385 g_free (fidata->uri);
3387 if (cache_stream != NULL) {
3390 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3393 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3394 if (G_UNLIKELY (nb_read < 0)) {
3396 } else if (G_LIKELY (nb_read > 0)) {
3397 gssize nb_written = 0;
3399 while (G_UNLIKELY (nb_written < nb_read)) {
3402 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3403 nb_read - nb_written);
3404 if (G_UNLIKELY (len < 0))
3410 tny_stream_close (cache_stream);
3411 g_object_unref (cache_stream);
3414 tny_stream_close (fidata->output_stream);
3415 g_object_unref (fidata->output_stream);
3417 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3423 on_fetch_image (ModestMsgView *msgview,
3426 ModestMsgViewWindow *window)
3428 const gchar *current_account;
3429 ModestMsgViewWindowPrivate *priv;
3430 FetchImageData *fidata;
3432 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3434 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3436 fidata = g_slice_new0 (FetchImageData);
3437 fidata->msg_view = g_object_ref (msgview);
3438 fidata->window = g_object_ref (window);
3439 fidata->uri = g_strdup (uri);
3440 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3441 fidata->output_stream = g_object_ref (stream);
3443 priv->fetching_images++;
3444 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3445 g_object_unref (fidata->output_stream);
3446 g_free (fidata->cache_id);
3447 g_free (fidata->uri);
3448 g_object_unref (fidata->msg_view);
3449 g_slice_free (FetchImageData, fidata);
3450 tny_stream_close (stream);
3451 priv->fetching_images--;
3452 update_progress_hint (window);
3455 update_progress_hint (window);
3461 setup_menu (ModestMsgViewWindow *self)
3463 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3465 /* Settings menu buttons */
3466 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3467 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3468 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3470 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3471 dngettext(GETTEXT_PACKAGE,
3472 "mcen_me_move_message",
3473 "mcen_me_move_messages",
3476 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3477 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3479 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3480 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3481 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3483 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3484 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3485 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3487 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3488 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3489 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3491 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3492 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3493 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3494 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3495 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3496 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3498 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3499 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3500 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3501 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3502 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3503 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3505 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3506 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3507 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3511 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3513 ModestMsgViewWindowPrivate *priv;
3514 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3515 GSList *recipients = NULL;
3517 gboolean contacts_to_add = FALSE;
3519 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3523 header = modest_msg_view_window_get_header (self);
3526 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3527 g_object_unref (header);
3529 recipients = modest_tny_msg_get_all_recipients_list (msg);
3530 g_object_unref (msg);
3533 if (recipients != NULL) {
3534 GtkWidget *picker_dialog;
3535 GtkWidget *selector;
3537 gchar *selected = NULL;
3539 selector = hildon_touch_selector_new_text ();
3540 g_object_ref (selector);
3542 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3543 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3544 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3545 (const gchar *) node->data);
3546 contacts_to_add = TRUE;
3550 if (contacts_to_add) {
3553 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3554 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3556 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3557 HILDON_TOUCH_SELECTOR (selector));
3559 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3561 if (picker_result == GTK_RESPONSE_OK) {
3562 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3564 gtk_widget_destroy (picker_dialog);
3567 modest_address_book_add_address (selected, (GtkWindow *) self);
3572 g_object_unref (selector);
3577 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3581 _modest_msg_view_window_map_event (GtkWidget *widget,
3585 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3587 update_progress_hint (self);
3593 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3595 ModestMsgViewWindowPrivate *priv;
3596 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3598 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3602 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3604 ModestMsgViewWindowPrivate *priv;
3605 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3607 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3609 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3613 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3615 ModestMsgViewWindowPrivate *priv;
3616 const gchar *msg_uid;
3617 TnyHeader *header = NULL;
3618 TnyFolder *folder = NULL;
3620 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3622 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3624 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3628 folder = tny_header_get_folder (header);
3629 g_object_unref (header);
3634 msg_uid = modest_msg_view_window_get_message_uid (self);
3636 if (!message_reader (self, priv, NULL, msg_uid, folder, priv->row_reference))
3637 g_warning ("Shouldn't happen, trying to reload a message failed");
3639 g_object_unref (folder);
3643 update_branding (ModestMsgViewWindow *self)
3645 const gchar *account;
3646 const gchar *mailbox;
3647 ModestAccountMgr *mgr;
3648 ModestProtocol *protocol = NULL;
3649 gchar *service_name = NULL;
3650 const GdkPixbuf *service_icon = NULL;
3651 ModestMsgViewWindowPrivate *priv;
3653 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3655 account = modest_window_get_active_account (MODEST_WINDOW (self));
3656 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3658 mgr = modest_runtime_get_account_mgr ();
3660 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3661 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3662 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3664 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3665 account, mailbox, MODEST_ICON_SIZE_SMALL);
3669 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3670 g_free (service_name);