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),
2693 TNY_STREAM (temp_stream),
2694 on_decode_to_stream_async_handler,
2700 if (!decode_in_provider)
2701 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2702 on_decode_to_stream_async_handler,
2705 g_object_unref (temp_stream);
2706 /* NOTE: files in the temporary area will be automatically
2707 * cleaned after some time if they are no longer in use */
2710 const gchar *content_type;
2711 /* the file may already exist but it isn't writable,
2712 * let's try to open it anyway */
2713 content_type = tny_mime_part_get_content_type (mime_part);
2714 modest_platform_activate_file (filepath, content_type);
2716 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2717 show_error_banner = TRUE;
2722 if (show_error_banner)
2723 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2724 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2725 ModestWindowMgr *mgr;
2726 ModestWindow *msg_win = NULL;
2727 TnyMsg *current_msg;
2731 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2732 mgr = modest_runtime_get_window_mgr ();
2733 header = tny_msg_get_header (TNY_MSG (current_msg));
2734 found = modest_window_mgr_find_registered_message_uid (mgr,
2739 g_debug ("window for this body is already being created");
2742 /* it's not found, so create a new window for it */
2743 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2744 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2745 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2747 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2749 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2750 account, mailbox, attachment_uid);
2752 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2753 modest_window_get_zoom (MODEST_WINDOW (window)));
2754 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2755 gtk_widget_show_all (GTK_WIDGET (msg_win));
2757 gtk_widget_destroy (GTK_WIDGET (msg_win));
2759 g_object_unref (current_msg);
2761 /* message attachment */
2762 TnyHeader *header = NULL;
2763 ModestWindowMgr *mgr;
2764 ModestWindow *msg_win = NULL;
2767 header = tny_msg_get_header (TNY_MSG (mime_part));
2768 mgr = modest_runtime_get_window_mgr ();
2769 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2772 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2773 * thus, we don't do anything */
2774 g_debug ("window for is already being created");
2776 /* it's not found, so create a new window for it */
2777 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2778 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2779 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2781 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2782 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2783 mailbox, attachment_uid);
2784 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2785 modest_window_get_zoom (MODEST_WINDOW (window)));
2786 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2787 gtk_widget_show_all (GTK_WIDGET (msg_win));
2789 gtk_widget_destroy (GTK_WIDGET (msg_win));
2795 g_free (attachment_uid);
2797 g_object_unref (mime_part);
2809 GnomeVFSResult result;
2811 ModestMsgViewWindow *window;
2814 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2815 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2816 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2817 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2820 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2824 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2825 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2826 g_free (pair->filename);
2827 g_object_unref (pair->part);
2828 g_slice_free (SaveMimePartPair, pair);
2830 g_list_free (info->pairs);
2833 g_object_unref (info->window);
2834 info->window = NULL;
2836 g_slice_free (SaveMimePartInfo, info);
2841 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2843 /* This is a GDK lock because we are an idle callback and
2844 * hildon_banner_show_information is or does Gtk+ code */
2846 gdk_threads_enter (); /* CHECKED */
2847 if (info->result == GNOME_VFS_OK) {
2848 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2849 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2852 /* Check if the uri belongs to the external mmc */
2853 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2854 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2856 msg = g_strdup (_KR("cerm_memory_card_full"));
2857 modest_platform_information_banner (NULL, NULL, msg);
2860 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2862 save_mime_part_info_free (info, FALSE);
2863 gdk_threads_leave (); /* CHECKED */
2869 save_mime_part_to_file (SaveMimePartInfo *info)
2871 GnomeVFSHandle *handle;
2873 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2875 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2876 if (info->result == GNOME_VFS_OK) {
2877 GError *error = NULL;
2878 gboolean decode_in_provider;
2880 ModestAccountMgr *mgr;
2881 const gchar *account;
2882 ModestProtocol *protocol = NULL;
2884 stream = tny_vfs_stream_new (handle);
2886 decode_in_provider = FALSE;
2887 mgr = modest_runtime_get_account_mgr ();
2888 account = modest_window_get_active_account (MODEST_WINDOW (info->window));
2889 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
2890 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
2891 decode_in_provider =
2892 modest_account_protocol_decode_part_to_stream (
2893 MODEST_ACCOUNT_PROTOCOL (protocol),
2901 if (!decode_in_provider)
2902 written = tny_mime_part_decode_to_stream (pair->part, stream, &error);
2905 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2907 if ((error->domain == TNY_ERROR_DOMAIN) &&
2908 (error->code == TNY_IO_ERROR_WRITE) &&
2909 (errno == ENOSPC)) {
2910 info->result = GNOME_VFS_ERROR_NO_SPACE;
2912 info->result = GNOME_VFS_ERROR_IO;
2915 g_object_unref (G_OBJECT (stream));
2917 g_warning ("Could not create save attachment %s: %s\n",
2918 pair->filename, gnome_vfs_result_to_string (info->result));
2921 /* Go on saving remaining files */
2922 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2923 if (info->pairs != NULL) {
2924 save_mime_part_to_file (info);
2926 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2933 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2934 SaveMimePartInfo *info)
2936 gboolean is_ok = TRUE;
2937 gint replaced_files = 0;
2938 const GList *files = info->pairs;
2939 const GList *iter, *to_replace = NULL;
2941 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2942 SaveMimePartPair *pair = iter->data;
2943 gchar *unescaped = g_uri_unescape_string (pair->filename, NULL);
2945 if (modest_utils_file_exists (unescaped)) {
2947 if (replaced_files == 1)
2952 if (replaced_files) {
2955 if (replaced_files == 1) {
2956 SaveMimePartPair *pair = to_replace->data;
2957 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2958 gchar *escaped_basename, *message;
2960 escaped_basename = g_uri_unescape_string (basename, NULL);
2961 message = g_strdup_printf ("%s\n%s",
2962 _FM("docm_nc_replace_file"),
2963 (escaped_basename) ? escaped_basename : "");
2964 response = modest_platform_run_confirmation_dialog (parent, message);
2966 g_free (escaped_basename);
2968 response = modest_platform_run_confirmation_dialog (parent,
2969 _FM("docm_nc_replace_multiple"));
2971 if (response != GTK_RESPONSE_OK)
2976 save_mime_part_info_free (info, TRUE);
2978 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2983 typedef struct _SaveAttachmentsInfo {
2984 TnyList *attachments_list;
2985 ModestMsgViewWindow *window;
2986 } SaveAttachmentsInfo;
2989 save_attachments_response (GtkDialog *dialog,
2993 TnyList *mime_parts;
2995 GList *files_to_save = NULL;
2996 gchar *current_folder;
2997 SaveAttachmentsInfo *sa_info = (SaveAttachmentsInfo *) user_data;
2999 mime_parts = TNY_LIST (sa_info->attachments_list);
3001 if (arg1 != GTK_RESPONSE_OK)
3004 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3005 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
3006 if (current_folder && *current_folder != '\0') {
3008 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
3009 current_folder,&err);
3011 g_debug ("Error storing latest used folder: %s", err->message);
3015 g_free (current_folder);
3017 if (!modest_utils_folder_writable (chooser_uri)) {
3018 const gchar *err_msg;
3020 #ifdef MODEST_PLATFORM_MAEMO
3021 if (modest_maemo_utils_in_usb_mode ()) {
3022 err_msg = dgettext ("hildon-status-bar-usb", "usbh_ib_mmc_usb_connected");
3024 err_msg = _FM("sfil_ib_readonly_location");
3027 err_msg = _FM("sfil_ib_readonly_location");
3029 hildon_banner_show_information (NULL, NULL, err_msg);
3033 iter = tny_list_create_iterator (mime_parts);
3034 while (!tny_iterator_is_done (iter)) {
3035 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3037 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
3038 !tny_mime_part_is_purged (mime_part) &&
3039 (tny_mime_part_get_filename (mime_part) != NULL)) {
3040 SaveMimePartPair *pair;
3042 pair = g_slice_new0 (SaveMimePartPair);
3044 if (tny_list_get_length (mime_parts) > 1) {
3046 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
3047 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
3050 pair->filename = g_strdup (chooser_uri);
3052 pair->part = mime_part;
3053 files_to_save = g_list_prepend (files_to_save, pair);
3055 tny_iterator_next (iter);
3057 g_object_unref (iter);
3060 if (files_to_save != NULL) {
3061 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
3062 info->pairs = files_to_save;
3063 info->result = TRUE;
3064 info->uri = g_strdup (chooser_uri);
3065 info->window = g_object_ref (sa_info->window);
3066 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
3068 g_free (chooser_uri);
3071 /* Free and close the dialog */
3072 g_object_unref (mime_parts);
3073 g_object_unref (sa_info->window);
3074 g_slice_free (SaveAttachmentsInfo, sa_info);
3075 gtk_widget_destroy (GTK_WIDGET (dialog));
3079 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
3080 TnyList *mime_parts)
3082 ModestMsgViewWindowPrivate *priv;
3083 GtkWidget *save_dialog = NULL;
3084 gchar *conf_folder = NULL;
3085 gchar *filename = NULL;
3086 gchar *save_multiple_str = NULL;
3087 const gchar *root_folder = "file:///";
3089 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3090 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3092 if (mime_parts == NULL) {
3093 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3094 * selection available */
3095 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3096 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3097 g_object_unref (mime_parts);
3100 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3102 g_object_unref (mime_parts);
3108 g_object_ref (mime_parts);
3111 /* prepare dialog */
3112 if (tny_list_get_length (mime_parts) == 1) {
3114 /* only one attachment selected */
3115 iter = tny_list_create_iterator (mime_parts);
3116 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3117 g_object_unref (iter);
3118 if (!modest_tny_mime_part_is_msg (mime_part) &&
3119 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3120 !tny_mime_part_is_purged (mime_part)) {
3121 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3123 /* TODO: show any error? */
3124 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3125 g_object_unref (mime_parts);
3128 g_object_unref (mime_part);
3130 gint num = tny_list_get_length (mime_parts);
3131 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3132 "sfil_va_number_of_objects_attachment",
3133 "sfil_va_number_of_objects_attachments",
3137 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3138 GTK_FILE_CHOOSER_ACTION_SAVE);
3140 /* Get last used folder */
3141 conf_folder = modest_conf_get_string (modest_runtime_get_conf (),
3142 MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3144 /* File chooser stops working if we select "file:///" as current folder */
3145 if (conf_folder && g_ascii_strcasecmp (root_folder, conf_folder) != 0) {
3146 g_free (conf_folder);
3150 if (conf_folder && conf_folder[0] != '\0') {
3151 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3154 /* Set the default folder to documents folder */
3155 docs_folder = (gchar *) g_strdup(g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
3158 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3160 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3161 g_free (docs_folder);
3163 g_free (conf_folder);
3167 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3172 /* if multiple, set multiple string */
3173 if (save_multiple_str) {
3174 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3175 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3176 g_free (save_multiple_str);
3179 /* We must run this asynchronously, because the hildon dialog
3180 performs a gtk_dialog_run by itself which leads to gdk
3182 SaveAttachmentsInfo *sa_info;
3183 sa_info = g_slice_new (SaveAttachmentsInfo);
3184 sa_info->attachments_list = mime_parts;
3185 sa_info->window = g_object_ref (window);
3186 g_signal_connect (save_dialog, "response",
3187 G_CALLBACK (save_attachments_response), sa_info);
3189 gtk_widget_show_all (save_dialog);
3193 show_remove_attachment_information (gpointer userdata)
3195 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3196 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3198 /* We're outside the main lock */
3199 gdk_threads_enter ();
3201 if (priv->remove_attachment_banner != NULL) {
3202 gtk_widget_destroy (priv->remove_attachment_banner);
3203 g_object_unref (priv->remove_attachment_banner);
3206 priv->remove_attachment_banner = g_object_ref (
3207 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3209 gdk_threads_leave ();
3215 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3217 ModestMsgViewWindowPrivate *priv;
3218 TnyList *mime_parts = NULL, *tmp;
3219 gchar *confirmation_message;
3225 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3226 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3228 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3229 * because we don't have selection
3231 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3233 /* Remove already purged messages from mime parts list. We use
3234 a copy of the list to remove items in the original one */
3235 tmp = tny_list_copy (mime_parts);
3236 iter = tny_list_create_iterator (tmp);
3237 while (!tny_iterator_is_done (iter)) {
3238 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3239 if (tny_mime_part_is_purged (part))
3240 tny_list_remove (mime_parts, (GObject *) part);
3242 g_object_unref (part);
3243 tny_iterator_next (iter);
3245 g_object_unref (tmp);
3246 g_object_unref (iter);
3248 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3249 tny_list_get_length (mime_parts) == 0) {
3250 g_object_unref (mime_parts);
3254 n_attachments = tny_list_get_length (mime_parts);
3255 if (n_attachments == 1) {
3259 iter = tny_list_create_iterator (mime_parts);
3260 part = (TnyMimePart *) tny_iterator_get_current (iter);
3261 g_object_unref (iter);
3262 if (modest_tny_mime_part_is_msg (part)) {
3264 header = tny_msg_get_header (TNY_MSG (part));
3265 filename = tny_header_dup_subject (header);
3266 g_object_unref (header);
3267 if (filename == NULL)
3268 filename = g_strdup (_("mail_va_no_subject"));
3270 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3272 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3274 g_object_unref (part);
3276 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3277 "mcen_nc_purge_files_text",
3278 n_attachments), n_attachments);
3280 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3281 confirmation_message);
3282 g_free (confirmation_message);
3284 if (response != GTK_RESPONSE_OK) {
3285 g_object_unref (mime_parts);
3289 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3291 iter = tny_list_create_iterator (mime_parts);
3292 while (!tny_iterator_is_done (iter)) {
3295 part = (TnyMimePart *) tny_iterator_get_current (iter);
3296 tny_mime_part_set_purged (TNY_MIME_PART (part));
3297 g_object_unref (part);
3298 tny_iterator_next (iter);
3300 g_object_unref (iter);
3302 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3303 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3304 tny_msg_rewrite_cache (msg);
3305 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3306 g_object_unref (msg);
3307 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3309 g_object_unref (mime_parts);
3311 if (priv->purge_timeout > 0) {
3312 g_source_remove (priv->purge_timeout);
3313 priv->purge_timeout = 0;
3316 if (priv->remove_attachment_banner) {
3317 gtk_widget_destroy (priv->remove_attachment_banner);
3318 g_object_unref (priv->remove_attachment_banner);
3319 priv->remove_attachment_banner = NULL;
3325 update_window_title (ModestMsgViewWindow *window)
3327 ModestMsgViewWindowPrivate *priv;
3329 TnyHeader *header = NULL;
3330 gchar *subject = NULL;
3332 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3334 /* Note that if the window is closed while we're retrieving
3335 the message, this widget could de deleted */
3336 if (!priv->msg_view)
3339 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3341 if (priv->other_body) {
3344 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3346 g_strstrip (description);
3347 subject = description;
3349 } else if (msg != NULL) {
3350 header = tny_msg_get_header (msg);
3351 subject = tny_header_dup_subject (header);
3352 g_object_unref (header);
3353 g_object_unref (msg);
3356 if ((subject == NULL)||(subject[0] == '\0')) {
3358 subject = g_strdup (_("mail_va_no_subject"));
3361 gtk_window_set_title (GTK_WINDOW (window), subject);
3366 on_move_focus (GtkWidget *widget,
3367 GtkDirectionType direction,
3370 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3374 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3376 GnomeVFSResult result;
3377 GnomeVFSHandle *handle = NULL;
3378 GnomeVFSFileInfo *info = NULL;
3381 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3382 if (result != GNOME_VFS_OK) {
3387 info = gnome_vfs_file_info_new ();
3388 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3389 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3390 /* We put a "safe" default size for going to cache */
3391 *expected_size = (300*1024);
3393 *expected_size = info->size;
3395 gnome_vfs_file_info_unref (info);
3397 stream = tny_vfs_stream_new (handle);
3406 TnyStream *output_stream;
3407 GtkWidget *msg_view;
3412 on_fetch_image_idle_refresh_view (gpointer userdata)
3415 FetchImageData *fidata = (FetchImageData *) userdata;
3417 gdk_threads_enter ();
3418 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3419 ModestMsgViewWindowPrivate *priv;
3421 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3422 priv->fetching_images--;
3423 gtk_widget_queue_draw (fidata->msg_view);
3424 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3426 gdk_threads_leave ();
3428 g_object_unref (fidata->msg_view);
3429 g_object_unref (fidata->window);
3430 g_slice_free (FetchImageData, fidata);
3435 on_fetch_image_thread (gpointer userdata)
3437 FetchImageData *fidata = (FetchImageData *) userdata;
3438 TnyStreamCache *cache;
3439 TnyStream *cache_stream;
3441 cache = modest_runtime_get_images_cache ();
3443 tny_stream_cache_get_stream (cache,
3445 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3446 (gpointer) fidata->uri);
3447 g_free (fidata->cache_id);
3448 g_free (fidata->uri);
3450 if (cache_stream != NULL) {
3453 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3456 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3457 if (G_UNLIKELY (nb_read < 0)) {
3459 } else if (G_LIKELY (nb_read > 0)) {
3460 gssize nb_written = 0;
3462 while (G_UNLIKELY (nb_written < nb_read)) {
3465 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3466 nb_read - nb_written);
3467 if (G_UNLIKELY (len < 0))
3473 tny_stream_close (cache_stream);
3474 g_object_unref (cache_stream);
3477 tny_stream_close (fidata->output_stream);
3478 g_object_unref (fidata->output_stream);
3480 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3486 on_fetch_image (ModestMsgView *msgview,
3489 ModestMsgViewWindow *window)
3491 const gchar *current_account;
3492 ModestMsgViewWindowPrivate *priv;
3493 FetchImageData *fidata;
3495 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3497 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3499 fidata = g_slice_new0 (FetchImageData);
3500 fidata->msg_view = g_object_ref (msgview);
3501 fidata->window = g_object_ref (window);
3502 fidata->uri = g_strdup (uri);
3503 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3504 fidata->output_stream = g_object_ref (stream);
3506 priv->fetching_images++;
3507 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3508 g_object_unref (fidata->output_stream);
3509 g_free (fidata->cache_id);
3510 g_free (fidata->uri);
3511 g_object_unref (fidata->msg_view);
3512 g_slice_free (FetchImageData, fidata);
3513 tny_stream_close (stream);
3514 priv->fetching_images--;
3515 update_progress_hint (window);
3518 update_progress_hint (window);
3524 setup_menu (ModestMsgViewWindow *self)
3526 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3528 /* Settings menu buttons */
3529 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3530 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3531 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3533 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3534 dngettext(GETTEXT_PACKAGE,
3535 "mcen_me_move_message",
3536 "mcen_me_move_messages",
3539 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3540 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3542 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3543 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3544 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3546 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3547 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3548 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3550 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3551 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3552 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3554 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3555 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3556 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3557 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3558 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3559 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3561 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3562 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3563 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3564 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3565 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3566 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3568 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3569 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3570 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3574 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3576 ModestMsgViewWindowPrivate *priv;
3577 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3578 GSList *recipients = NULL;
3580 gboolean contacts_to_add = FALSE;
3582 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3586 header = modest_msg_view_window_get_header (self);
3589 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3590 g_object_unref (header);
3592 recipients = modest_tny_msg_get_all_recipients_list (msg);
3593 g_object_unref (msg);
3596 if (recipients != NULL) {
3597 GtkWidget *picker_dialog;
3598 GtkWidget *selector;
3600 gchar *selected = NULL;
3602 selector = hildon_touch_selector_new_text ();
3603 g_object_ref (selector);
3605 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3606 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3607 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3608 (const gchar *) node->data);
3609 contacts_to_add = TRUE;
3613 if (contacts_to_add) {
3616 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3617 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3619 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3620 HILDON_TOUCH_SELECTOR (selector));
3622 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3624 if (picker_result == GTK_RESPONSE_OK) {
3625 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3627 gtk_widget_destroy (picker_dialog);
3630 modest_address_book_add_address (selected, (GtkWindow *) self);
3635 g_object_unref (selector);
3640 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3644 _modest_msg_view_window_map_event (GtkWidget *widget,
3648 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3650 update_progress_hint (self);
3656 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3658 ModestMsgViewWindowPrivate *priv;
3659 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3661 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3665 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3667 ModestMsgViewWindowPrivate *priv;
3668 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3670 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3672 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3676 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3678 ModestMsgViewWindowPrivate *priv;
3679 const gchar *msg_uid;
3680 TnyHeader *header = NULL;
3681 TnyFolder *folder = NULL;
3683 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3685 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3687 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3691 folder = tny_header_get_folder (header);
3692 g_object_unref (header);
3697 msg_uid = modest_msg_view_window_get_message_uid (self);
3699 if (!message_reader (self, priv, NULL, msg_uid, folder, priv->row_reference))
3700 g_warning ("Shouldn't happen, trying to reload a message failed");
3702 g_object_unref (folder);
3706 update_branding (ModestMsgViewWindow *self)
3708 const gchar *account;
3709 const gchar *mailbox;
3710 ModestAccountMgr *mgr;
3711 ModestProtocol *protocol = NULL;
3712 gchar *service_name = NULL;
3713 const GdkPixbuf *service_icon = NULL;
3714 ModestMsgViewWindowPrivate *priv;
3716 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3718 account = modest_window_get_active_account (MODEST_WINDOW (self));
3719 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3721 mgr = modest_runtime_get_account_mgr ();
3723 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3724 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3725 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3727 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3728 account, mailbox, MODEST_ICON_SIZE_SMALL);
3732 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3733 g_free (service_name);