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 ModestAccountMgr *mgr;
2671 DecodeAsyncHelper *helper;
2672 gboolean decode_in_provider;
2673 ModestProtocol *protocol;
2674 const gchar *account;
2676 /* Activate progress hint */
2677 set_progress_hint (window, TRUE);
2679 helper = g_slice_new0 (DecodeAsyncHelper);
2680 helper->self = g_object_ref (window);
2681 helper->file_path = g_strdup (filepath);
2683 decode_in_provider = FALSE;
2684 mgr = modest_runtime_get_account_mgr ();
2685 account = modest_window_get_active_account (MODEST_WINDOW (window));
2686 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2687 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2688 decode_in_provider =
2689 modest_account_protocol_decode_part_to_stream_async (
2690 MODEST_ACCOUNT_PROTOCOL (protocol),
2692 TNY_STREAM (temp_stream),
2693 on_decode_to_stream_async_handler,
2699 if (!decode_in_provider)
2700 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2701 on_decode_to_stream_async_handler,
2704 g_object_unref (temp_stream);
2705 /* NOTE: files in the temporary area will be automatically
2706 * cleaned after some time if they are no longer in use */
2709 const gchar *content_type;
2710 /* the file may already exist but it isn't writable,
2711 * let's try to open it anyway */
2712 content_type = tny_mime_part_get_content_type (mime_part);
2713 modest_platform_activate_file (filepath, content_type);
2715 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2716 show_error_banner = TRUE;
2721 if (show_error_banner)
2722 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2723 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2724 ModestWindowMgr *mgr;
2725 ModestWindow *msg_win = NULL;
2726 TnyMsg *current_msg;
2730 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2731 mgr = modest_runtime_get_window_mgr ();
2732 header = tny_msg_get_header (TNY_MSG (current_msg));
2733 found = modest_window_mgr_find_registered_message_uid (mgr,
2738 g_debug ("window for this body is already being created");
2741 /* it's not found, so create a new window for it */
2742 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2743 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2744 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2746 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2748 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2749 account, mailbox, attachment_uid);
2751 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2752 modest_window_get_zoom (MODEST_WINDOW (window)));
2753 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2754 gtk_widget_show_all (GTK_WIDGET (msg_win));
2756 gtk_widget_destroy (GTK_WIDGET (msg_win));
2758 g_object_unref (current_msg);
2760 /* message attachment */
2761 TnyHeader *header = NULL;
2762 ModestWindowMgr *mgr;
2763 ModestWindow *msg_win = NULL;
2766 header = tny_msg_get_header (TNY_MSG (mime_part));
2767 mgr = modest_runtime_get_window_mgr ();
2768 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2771 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2772 * thus, we don't do anything */
2773 g_debug ("window for is already being created");
2775 /* it's not found, so create a new window for it */
2776 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2777 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2778 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2780 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2781 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2782 mailbox, attachment_uid);
2783 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2784 modest_window_get_zoom (MODEST_WINDOW (window)));
2785 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2786 gtk_widget_show_all (GTK_WIDGET (msg_win));
2788 gtk_widget_destroy (GTK_WIDGET (msg_win));
2794 g_free (attachment_uid);
2796 g_object_unref (mime_part);
2808 GnomeVFSResult result;
2810 ModestMsgViewWindow *window;
2813 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2814 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2815 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2816 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2819 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2823 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2824 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2825 g_free (pair->filename);
2826 g_object_unref (pair->part);
2827 g_slice_free (SaveMimePartPair, pair);
2829 g_list_free (info->pairs);
2832 g_object_unref (info->window);
2833 info->window = NULL;
2835 g_slice_free (SaveMimePartInfo, info);
2840 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2842 /* This is a GDK lock because we are an idle callback and
2843 * hildon_banner_show_information is or does Gtk+ code */
2845 gdk_threads_enter (); /* CHECKED */
2846 if (info->result == GNOME_VFS_OK) {
2847 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2848 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2851 /* Check if the uri belongs to the external mmc */
2852 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2853 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2855 msg = g_strdup (_KR("cerm_memory_card_full"));
2856 modest_platform_information_banner (NULL, NULL, msg);
2859 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2861 save_mime_part_info_free (info, FALSE);
2862 gdk_threads_leave (); /* CHECKED */
2868 save_mime_part_to_file (SaveMimePartInfo *info)
2870 GnomeVFSHandle *handle;
2872 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2874 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2875 if (info->result == GNOME_VFS_OK) {
2876 GError *error = NULL;
2877 gboolean decode_in_provider;
2879 ModestAccountMgr *mgr;
2880 const gchar *account;
2881 ModestProtocol *protocol = NULL;
2883 stream = tny_vfs_stream_new (handle);
2885 decode_in_provider = FALSE;
2886 mgr = modest_runtime_get_account_mgr ();
2887 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2888 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2889 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2890 decode_in_provider =
2891 modest_account_protocol_decode_part_to_stream (
2892 MODEST_ACCOUNT_PROTOCOL (protocol),
2899 if (!decode_in_provider)
2900 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2903 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2905 if ((error->domain == TNY_ERROR_DOMAIN) &&
2906 (error->code == TNY_IO_ERROR_WRITE) &&
2907 (errno == ENOSPC)) {
2908 info->result = GNOME_VFS_ERROR_NO_SPACE;
2910 info->result = GNOME_VFS_ERROR_IO;
2913 g_object_unref (G_OBJECT (stream));
2915 g_warning ("Could not create save attachment %s: %s\n",
2916 pair->filename, gnome_vfs_result_to_string (info->result));
2919 /* Go on saving remaining files */
2920 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2921 if (info->pairs != NULL) {
2922 save_mime_part_to_file (info);
2924 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2931 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2932 SaveMimePartInfo *info)
2934 gboolean is_ok = TRUE;
2935 gint replaced_files = 0;
2936 const GList *files = info->pairs;
2937 const GList *iter, *to_replace = NULL;
2939 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2940 SaveMimePartPair *pair = iter->data;
2941 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
2943 if (modest_utils_file_exists (unescaped)) {
2945 if (replaced_files == 1)
2950 if (replaced_files) {
2953 if (replaced_files == 1) {
2954 SaveMimePartPair *pair = to_replace->data;
2955 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2956 gchar *escaped_basename, *message;
2958 escaped_basename = g_uri_unescape_string (basename, NULL);
2959 message = g_strdup_printf ("%s\n%s",
2960 _FM("docm_nc_replace_file"),
2961 (escaped_basename) ? escaped_basename : "");
2962 response = modest_platform_run_confirmation_dialog (parent, message);
2964 g_free (escaped_basename);
2966 response = modest_platform_run_confirmation_dialog (parent,
2967 _FM("docm_nc_replace_multiple"));
2969 if (response != GTK_RESPONSE_OK)
2974 save_mime_part_info_free (info, TRUE);
2976 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2981 typedef struct _SaveAttachmentsInfo {
2982 TnyList *attachments_list;
2983 ModestMsgViewWindow *window;
2984 } SaveAttachmentsInfo;
2987 save_attachments_response (GtkDialog *dialog,
2991 TnyList *mime_parts;
2993 GList *files_to_save = NULL;
2994 gchar *current_folder;
2995 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
2997 mime_parts = TNY_LIST (sa_info->attachments_list);
2999 if (arg1 != GTK_RESPONSE_OK)
3002 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3003 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3004 if (current_folder && *current_folder != '\0') {
3006 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3007 current_folder,&err);
3009 g_debug ("Error storing latest used folder: %s", err->message);
3013 g_free (current_folder);
3015 if (!modest_utils_folder_writable (chooser_uri)) {
3016 const gchar *err_msg;
3018 #ifdef MODEST_PLATFORM_MAEMO
3019 if (modest_maemo_utils_in_usb_mode ()) {
3020 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3022 err_msg = _FM("sfil_ib_readonly_location");
3025 err_msg = _FM("sfil_ib_readonly_location");
3027 hildon_banner_show_information (NULL, NULL, err_msg);
3031 iter = tny_list_create_iterator (mime_parts);
3032 while (!tny_iterator_is_done (iter)) {
3033 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3035 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3036 !tny_mime_part_is_purged (mime_part) &&
3037 (tny_mime_part_get_filename (mime_part) != NULL)) {
3038 SaveMimePartPair *pair;
3040 pair = g_slice_new0 (SaveMimePartPair);
3042 if (tny_list_get_length (mime_parts) > 1) {
3044 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3045 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3048 pair->filename = g_strdup (chooser_uri);
3050 pair->part = mime_part;
3051 files_to_save = g_list_prepend (files_to_save, pair);
3053 tny_iterator_next (iter);
3055 g_object_unref (iter);
3058 if (files_to_save != NULL) {
3059 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3060 info->pairs = files_to_save;
3061 info->result = TRUE;
3062 info->uri = g_strdup (chooser_uri);
3063 info->window = g_object_ref (sa_info->window);
3064 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3066 g_free (chooser_uri);
3069 /* Free and close the dialog */
3070 g_object_unref (mime_parts);
3071 g_object_unref (sa_info->window);
3072 g_slice_free (SaveAttachmentsInfo, sa_info);
3073 gtk_widget_destroy (GTK_WIDGET (dialog));
3077 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3078 TnyList *mime_parts)
3080 ModestMsgViewWindowPrivate *priv;
3081 GtkWidget *save_dialog = NULL;
3082 gchar *conf_folder = NULL;
3083 gchar *filename = NULL;
3084 gchar *save_multiple_str = NULL;
3085 const gchar *root_folder = "file:///";
3087 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3088 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3090 if (mime_parts == NULL) {
3091 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3092 * selection available */
3093 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3094 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3095 g_object_unref (mime_parts);
3098 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3100 g_object_unref (mime_parts);
3106 g_object_ref (mime_parts);
3109 /* prepare dialog */
3110 if (tny_list_get_length (mime_parts) == 1) {
3112 /* only one attachment selected */
3113 iter = tny_list_create_iterator (mime_parts);
3114 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3115 g_object_unref (iter);
3116 if (!modest_tny_mime_part_is_msg (mime_part) &&
3117 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3118 !tny_mime_part_is_purged (mime_part)) {
3119 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3121 /* TODO: show any error? */
3122 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3123 g_object_unref (mime_parts);
3126 g_object_unref (mime_part);
3128 gint num = tny_list_get_length (mime_parts);
3129 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3130 "sfil_va_number_of_objects_attachment",
3131 "sfil_va_number_of_objects_attachments",
3135 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3136 GTK_FILE_CHOOSER_ACTION_SAVE);
3138 /* Get last used folder */
3139 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3140 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3142 /* File chooser stops working if we select "file:///" as current folder */
3143 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3144 g_free (conf_folder);
3148 if (conf_folder && conf_folder[0] != '\0') {
3149 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3152 /* Set the default folder to documents folder */
3153 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3156 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3158 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3159 g_free (docs_folder);
3161 g_free (conf_folder);
3165 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3170 /* if multiple, set multiple string */
3171 if (save_multiple_str) {
3172 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3173 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3174 g_free (save_multiple_str);
3177 /* We must run this asynchronously, because the hildon dialog
3178 performs a gtk_dialog_run by itself which leads to gdk
3180 SaveAttachmentsInfo *sa_info;
3181 sa_info = g_slice_new (SaveAttachmentsInfo);
3182 sa_info->attachments_list = mime_parts;
3183 sa_info->window = g_object_ref (window);
3184 g_signal_connect (save_dialog, "response",
3185 G_CALLBACK (save_attachments_response), sa_info);
3187 gtk_widget_show_all (save_dialog);
3191 show_remove_attachment_information (gpointer userdata)
3193 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3194 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3196 /* We're outside the main lock */
3197 gdk_threads_enter ();
3199 if (priv->remove_attachment_banner != NULL) {
3200 gtk_widget_destroy (priv->remove_attachment_banner);
3201 g_object_unref (priv->remove_attachment_banner);
3204 priv->remove_attachment_banner = g_object_ref (
3205 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3207 gdk_threads_leave ();
3213 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3215 ModestMsgViewWindowPrivate *priv;
3216 TnyList *mime_parts = NULL, *tmp;
3217 gchar *confirmation_message;
3223 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3224 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3226 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3227 * because we don't have selection
3229 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3231 /* Remove already purged messages from mime parts list. We use
3232 a copy of the list to remove items in the original one */
3233 tmp = tny_list_copy (mime_parts);
3234 iter = tny_list_create_iterator (tmp);
3235 while (!tny_iterator_is_done (iter)) {
3236 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3237 if (tny_mime_part_is_purged (part))
3238 tny_list_remove (mime_parts, (GObject *) part);
3240 g_object_unref (part);
3241 tny_iterator_next (iter);
3243 g_object_unref (tmp);
3244 g_object_unref (iter);
3246 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3247 tny_list_get_length (mime_parts) == 0) {
3248 g_object_unref (mime_parts);
3252 n_attachments = tny_list_get_length (mime_parts);
3253 if (n_attachments == 1) {
3257 iter = tny_list_create_iterator (mime_parts);
3258 part = (TnyMimePart *) tny_iterator_get_current (iter);
3259 g_object_unref (iter);
3260 if (modest_tny_mime_part_is_msg (part)) {
3262 header = tny_msg_get_header (TNY_MSG (part));
3263 filename = tny_header_dup_subject (header);
3264 g_object_unref (header);
3265 if (filename == NULL)
3266 filename = g_strdup (_("mail_va_no_subject"));
3268 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3270 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3272 g_object_unref (part);
3274 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3275 "mcen_nc_purge_files_text",
3276 n_attachments), n_attachments);
3278 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3279 confirmation_message);
3280 g_free (confirmation_message);
3282 if (response != GTK_RESPONSE_OK) {
3283 g_object_unref (mime_parts);
3287 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3289 iter = tny_list_create_iterator (mime_parts);
3290 while (!tny_iterator_is_done (iter)) {
3293 part = (TnyMimePart *) tny_iterator_get_current (iter);
3294 tny_mime_part_set_purged (TNY_MIME_PART (part));
3295 g_object_unref (part);
3296 tny_iterator_next (iter);
3298 g_object_unref (iter);
3300 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3301 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3302 tny_msg_rewrite_cache (msg);
3303 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3304 g_object_unref (msg);
3305 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3307 g_object_unref (mime_parts);
3309 if (priv->purge_timeout > 0) {
3310 g_source_remove (priv->purge_timeout);
3311 priv->purge_timeout = 0;
3314 if (priv->remove_attachment_banner) {
3315 gtk_widget_destroy (priv->remove_attachment_banner);
3316 g_object_unref (priv->remove_attachment_banner);
3317 priv->remove_attachment_banner = NULL;
3323 update_window_title (ModestMsgViewWindow *window)
3325 ModestMsgViewWindowPrivate *priv;
3327 TnyHeader *header = NULL;
3328 gchar *subject = NULL;
3330 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3332 /* Note that if the window is closed while we're retrieving
3333 the message, this widget could de deleted */
3334 if (!priv->msg_view)
3337 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3339 if (priv->other_body) {
3342 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3344 g_strstrip (description);
3345 subject = description;
3347 } else if (msg != NULL) {
3348 header = tny_msg_get_header (msg);
3349 subject = tny_header_dup_subject (header);
3350 g_object_unref (header);
3351 g_object_unref (msg);
3354 if ((subject == NULL)||(subject[0] == '\0')) {
3356 subject = g_strdup (_("mail_va_no_subject"));
3359 gtk_window_set_title (GTK_WINDOW (window), subject);
3364 on_move_focus (GtkWidget *widget,
3365 GtkDirectionType direction,
3368 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3372 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3374 GnomeVFSResult result;
3375 GnomeVFSHandle *handle = NULL;
3376 GnomeVFSFileInfo *info = NULL;
3379 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3380 if (result != GNOME_VFS_OK) {
3385 info = gnome_vfs_file_info_new ();
3386 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3387 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3388 /* We put a "safe" default size for going to cache */
3389 *expected_size = (300*1024);
3391 *expected_size = info->size;
3393 gnome_vfs_file_info_unref (info);
3395 stream = tny_vfs_stream_new (handle);
3404 TnyStream *output_stream;
3405 GtkWidget *msg_view;
3410 on_fetch_image_idle_refresh_view (gpointer userdata)
3413 FetchImageData *fidata = (FetchImageData *) userdata;
3415 gdk_threads_enter ();
3416 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3417 ModestMsgViewWindowPrivate *priv;
3419 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3420 priv->fetching_images--;
3421 gtk_widget_queue_draw (fidata->msg_view);
3422 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3424 gdk_threads_leave ();
3426 g_object_unref (fidata->msg_view);
3427 g_object_unref (fidata->window);
3428 g_slice_free (FetchImageData, fidata);
3433 on_fetch_image_thread (gpointer userdata)
3435 FetchImageData *fidata = (FetchImageData *) userdata;
3436 TnyStreamCache *cache;
3437 TnyStream *cache_stream;
3439 cache = modest_runtime_get_images_cache ();
3441 tny_stream_cache_get_stream (cache,
3443 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3444 (gpointer) fidata->uri);
3445 g_free (fidata->cache_id);
3446 g_free (fidata->uri);
3448 if (cache_stream != NULL) {
3451 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3454 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3455 if (G_UNLIKELY (nb_read < 0)) {
3457 } else if (G_LIKELY (nb_read > 0)) {
3458 gssize nb_written = 0;
3460 while (G_UNLIKELY (nb_written < nb_read)) {
3463 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3464 nb_read - nb_written);
3465 if (G_UNLIKELY (len < 0))
3471 tny_stream_close (cache_stream);
3472 g_object_unref (cache_stream);
3475 tny_stream_close (fidata->output_stream);
3476 g_object_unref (fidata->output_stream);
3478 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3484 on_fetch_image (ModestMsgView *msgview,
3487 ModestMsgViewWindow *window)
3489 const gchar *current_account;
3490 ModestMsgViewWindowPrivate *priv;
3491 FetchImageData *fidata;
3493 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3495 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3497 fidata = g_slice_new0 (FetchImageData);
3498 fidata->msg_view = g_object_ref (msgview);
3499 fidata->window = g_object_ref (window);
3500 fidata->uri = g_strdup (uri);
3501 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3502 fidata->output_stream = g_object_ref (stream);
3504 priv->fetching_images++;
3505 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3506 g_object_unref (fidata->output_stream);
3507 g_free (fidata->cache_id);
3508 g_free (fidata->uri);
3509 g_object_unref (fidata->msg_view);
3510 g_slice_free (FetchImageData, fidata);
3511 tny_stream_close (stream);
3512 priv->fetching_images--;
3513 update_progress_hint (window);
3516 update_progress_hint (window);
3522 setup_menu (ModestMsgViewWindow *self)
3524 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3526 /* Settings menu buttons */
3527 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3528 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3529 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3531 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3532 dngettext(GETTEXT_PACKAGE,
3533 "mcen_me_move_message",
3534 "mcen_me_move_messages",
3537 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3538 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3540 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3541 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3542 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3544 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3545 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3546 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3548 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3549 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3550 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3552 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3553 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3554 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3555 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3556 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3557 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3559 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3560 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3561 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3562 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3563 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3564 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3566 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3567 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3568 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3572 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3574 ModestMsgViewWindowPrivate *priv;
3575 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3576 GSList *recipients = NULL;
3578 gboolean contacts_to_add = FALSE;
3580 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3584 header = modest_msg_view_window_get_header (self);
3587 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3588 g_object_unref (header);
3590 recipients = modest_tny_msg_get_all_recipients_list (msg);
3591 g_object_unref (msg);
3594 if (recipients != NULL) {
3595 GtkWidget *picker_dialog;
3596 GtkWidget *selector;
3598 gchar *selected = NULL;
3600 selector = hildon_touch_selector_new_text ();
3601 g_object_ref (selector);
3603 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3604 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3605 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3606 (const gchar *) node->data);
3607 contacts_to_add = TRUE;
3611 if (contacts_to_add) {
3614 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3615 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3617 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3618 HILDON_TOUCH_SELECTOR (selector));
3620 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3622 if (picker_result == GTK_RESPONSE_OK) {
3623 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3625 gtk_widget_destroy (picker_dialog);
3628 modest_address_book_add_address (selected, (GtkWindow *) self);
3633 g_object_unref (selector);
3638 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3642 _modest_msg_view_window_map_event (GtkWidget *widget,
3646 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3648 update_progress_hint (self);
3654 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3656 ModestMsgViewWindowPrivate *priv;
3657 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3659 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3663 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3665 ModestMsgViewWindowPrivate *priv;
3666 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3668 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3670 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3674 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3676 ModestMsgViewWindowPrivate *priv;
3677 const gchar *msg_uid;
3678 TnyHeader *header = NULL;
3679 TnyFolder *folder = NULL;
3681 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3683 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3685 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3689 folder = tny_header_get_folder (header);
3690 g_object_unref (header);
3695 msg_uid = modest_msg_view_window_get_message_uid (self);
3697 if (!message_reader (self, priv, NULL, msg_uid, folder, priv->row_reference))
3698 g_warning ("Shouldn't happen, trying to reload a message failed");
3700 g_object_unref (folder);
3704 update_branding (ModestMsgViewWindow *self)
3706 const gchar *account;
3707 const gchar *mailbox;
3708 ModestAccountMgr *mgr;
3709 ModestProtocol *protocol = NULL;
3710 gchar *service_name = NULL;
3711 const GdkPixbuf *service_icon = NULL;
3712 ModestMsgViewWindowPrivate *priv;
3714 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3716 account = modest_window_get_active_account (MODEST_WINDOW (self));
3717 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3719 mgr = modest_runtime_get_account_mgr ();
3721 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3722 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3723 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3725 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3726 account, mailbox, MODEST_ICON_SIZE_SMALL);
3730 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3731 g_free (service_name);