1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
69 #include <modest-account-protocol.h>
71 #define MYDOCS_ENV "MYDOCSDIR"
72 #define DOCS_FOLDER ".documents"
74 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
75 struct _ModestMsgViewWindowPrivate {
78 GtkWidget *main_scroll;
79 GtkWidget *find_toolbar;
82 /* Progress observers */
83 GSList *progress_widgets;
86 GtkWidget *prev_toolitem;
87 GtkWidget *next_toolitem;
88 gboolean progress_hint;
91 /* Optimized view enabled */
92 gboolean optimized_view;
94 /* Whether this was created via the *_new_for_search_result() function. */
95 gboolean is_search_result;
97 /* Whether the message is in outbox */
100 /* A reference to the @model of the header view
101 * to allow selecting previous/next messages,
102 * if the message is currently selected in the header view.
104 const gchar *header_folder_id;
105 GtkTreeModel *header_model;
106 GtkTreeRowReference *row_reference;
107 GtkTreeRowReference *next_row_reference;
109 gulong clipboard_change_handler;
110 gulong queue_change_handler;
111 gulong account_removed_handler;
112 gulong row_changed_handler;
113 gulong row_deleted_handler;
114 gulong row_inserted_handler;
115 gulong rows_reordered_handler;
118 GtkWidget *remove_attachment_banner;
121 TnyMimePart *other_body;
126 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
127 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
128 static void modest_header_view_observer_init (ModestHeaderViewObserverIface *iface_class);
129 static void modest_msg_view_window_finalize (GObject *obj);
130 static void modest_msg_view_window_show_find_toolbar (GtkWidget *obj, gpointer data);
131 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
132 ModestMsgViewWindow *obj);
133 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
134 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
137 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
139 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
140 static void modest_msg_view_window_set_zoom (ModestWindow *window,
142 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
143 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
144 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
147 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
149 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
150 gboolean show_toolbar);
152 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
154 ModestMsgViewWindow *window);
156 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
159 ModestMsgViewWindow *window);
161 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
163 ModestMsgViewWindow *window);
165 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
166 GtkTreePath *tree_path,
167 GtkTreeIter *tree_iter,
168 ModestMsgViewWindow *window);
170 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
174 ModestMsgViewWindow *window);
176 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
178 const gchar *tny_folder_id);
180 static void on_queue_changed (ModestMailOperationQueue *queue,
181 ModestMailOperation *mail_op,
182 ModestMailOperationQueueNotification type,
183 ModestMsgViewWindow *self);
185 static void on_account_removed (TnyAccountStore *account_store,
189 static void on_move_focus (GtkWidget *widget,
190 GtkDirectionType direction,
193 static void view_msg_cb (ModestMailOperation *mail_op,
200 static void set_progress_hint (ModestMsgViewWindow *self,
203 static void update_window_title (ModestMsgViewWindow *window);
205 static void init_window (ModestMsgViewWindow *obj);
207 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
209 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
211 static gboolean on_fetch_image (ModestMsgView *msgview,
214 ModestMsgViewWindow *window);
216 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
217 GtkScrollType scroll_type,
220 static gboolean message_reader (ModestMsgViewWindow *window,
221 ModestMsgViewWindowPrivate *priv,
223 const gchar *msg_uid,
225 GtkTreeRowReference *row_reference);
227 static void setup_menu (ModestMsgViewWindow *self);
228 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
231 static void update_branding (ModestMsgViewWindow *self);
234 /* list my signals */
241 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
242 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
245 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
246 MODEST_TYPE_MSG_VIEW_WINDOW, \
247 ModestMsgViewWindowPrivate))
249 static GtkWindowClass *parent_class = NULL;
251 /* uncomment the following if you have defined any signals */
252 static guint signals[LAST_SIGNAL] = {0};
255 modest_msg_view_window_get_type (void)
257 static GType my_type = 0;
259 static const GTypeInfo my_info = {
260 sizeof(ModestMsgViewWindowClass),
261 NULL, /* base init */
262 NULL, /* base finalize */
263 (GClassInitFunc) modest_msg_view_window_class_init,
264 NULL, /* class finalize */
265 NULL, /* class data */
266 sizeof(ModestMsgViewWindow),
268 (GInstanceInitFunc) modest_msg_view_window_init,
271 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
272 "ModestMsgViewWindow",
275 static const GInterfaceInfo modest_header_view_observer_info =
277 (GInterfaceInitFunc) modest_header_view_observer_init,
278 NULL, /* interface_finalize */
279 NULL /* interface_data */
282 g_type_add_interface_static (my_type,
283 MODEST_TYPE_HEADER_VIEW_OBSERVER,
284 &modest_header_view_observer_info);
290 save_state (ModestWindow *self)
292 modest_widget_memory_save (modest_runtime_get_conf (),
294 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
298 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
299 GtkScrollType scroll_type,
303 ModestMsgViewWindowPrivate *priv;
304 gboolean return_value;
306 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
307 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
312 add_scroll_binding (GtkBindingSet *binding_set,
314 GtkScrollType scroll)
316 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
318 gtk_binding_entry_add_signal (binding_set, keyval, 0,
320 GTK_TYPE_SCROLL_TYPE, scroll,
321 G_TYPE_BOOLEAN, FALSE);
322 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
324 GTK_TYPE_SCROLL_TYPE, scroll,
325 G_TYPE_BOOLEAN, FALSE);
329 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
331 GObjectClass *gobject_class;
332 HildonWindowClass *hildon_window_class;
333 ModestWindowClass *modest_window_class;
334 GtkBindingSet *binding_set;
336 gobject_class = (GObjectClass*) klass;
337 hildon_window_class = (HildonWindowClass *) klass;
338 modest_window_class = (ModestWindowClass *) klass;
340 parent_class = g_type_class_peek_parent (klass);
341 gobject_class->finalize = modest_msg_view_window_finalize;
343 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
344 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
345 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
346 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
347 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
348 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
350 modest_window_class->save_state_func = save_state;
352 klass->scroll_child = modest_msg_view_window_scroll_child;
354 signals[MSG_CHANGED_SIGNAL] =
355 g_signal_new ("msg-changed",
356 G_TYPE_FROM_CLASS (gobject_class),
358 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
360 modest_marshal_VOID__POINTER_POINTER,
361 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
363 signals[SCROLL_CHILD_SIGNAL] =
364 g_signal_new ("scroll-child",
365 G_TYPE_FROM_CLASS (gobject_class),
366 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
367 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
369 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
370 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
372 binding_set = gtk_binding_set_by_class (klass);
373 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
374 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
375 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
376 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
377 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
378 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
380 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
384 static void modest_header_view_observer_init(
385 ModestHeaderViewObserverIface *iface_class)
387 iface_class->update_func = modest_msg_view_window_update_model_replaced;
391 modest_msg_view_window_init (ModestMsgViewWindow *obj)
393 ModestMsgViewWindowPrivate *priv;
394 ModestWindowPrivate *parent_priv = NULL;
395 GtkActionGroup *action_group = NULL;
396 GError *error = NULL;
398 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
399 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
400 parent_priv->ui_manager = gtk_ui_manager_new();
402 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
403 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
405 /* Add common actions */
406 gtk_action_group_add_actions (action_group,
407 modest_action_entries,
408 G_N_ELEMENTS (modest_action_entries),
410 gtk_action_group_add_toggle_actions (action_group,
411 msg_view_toggle_action_entries,
412 G_N_ELEMENTS (msg_view_toggle_action_entries),
415 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
416 g_object_unref (action_group);
418 /* Load the UI definition */
419 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
422 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
423 g_error_free (error);
428 /* Add accelerators */
429 gtk_window_add_accel_group (GTK_WINDOW (obj),
430 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
432 priv->is_search_result = FALSE;
433 priv->is_outbox = FALSE;
435 priv->msg_view = NULL;
436 priv->header_model = NULL;
437 priv->header_folder_id = NULL;
438 priv->clipboard_change_handler = 0;
439 priv->queue_change_handler = 0;
440 priv->account_removed_handler = 0;
441 priv->row_changed_handler = 0;
442 priv->row_deleted_handler = 0;
443 priv->row_inserted_handler = 0;
444 priv->rows_reordered_handler = 0;
445 priv->progress_hint = FALSE;
446 priv->fetching_images = 0;
448 priv->optimized_view = FALSE;
449 priv->purge_timeout = 0;
450 priv->remove_attachment_banner = NULL;
451 priv->msg_uid = NULL;
452 priv->other_body = NULL;
454 priv->sighandlers = NULL;
457 init_window (MODEST_MSG_VIEW_WINDOW(obj));
459 hildon_program_add_window (hildon_program_get_instance(),
465 update_progress_hint (ModestMsgViewWindow *self)
467 ModestMsgViewWindowPrivate *priv;
468 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
470 if (GTK_WIDGET_VISIBLE (self)) {
471 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self),
472 (priv->progress_hint || (priv->fetching_images > 0))?1:0);
477 set_progress_hint (ModestMsgViewWindow *self,
480 ModestWindowPrivate *parent_priv;
481 ModestMsgViewWindowPrivate *priv;
483 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
485 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
486 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
488 /* Sets current progress hint */
489 priv->progress_hint = enabled;
491 update_progress_hint (self);
497 init_window (ModestMsgViewWindow *obj)
499 GtkWidget *main_vbox;
500 ModestMsgViewWindowPrivate *priv;
502 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
504 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
505 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
506 main_vbox = gtk_vbox_new (FALSE, 6);
507 priv->main_scroll = hildon_pannable_area_new ();
508 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
509 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
510 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
512 /* NULL-ize fields if the window is destroyed */
513 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
515 gtk_widget_show_all (GTK_WIDGET(main_vbox));
519 modest_msg_view_window_disconnect_signals (ModestWindow *self)
521 ModestMsgViewWindowPrivate *priv;
522 GtkWidget *header_view = NULL;
523 GtkWindow *parent_window = NULL;
525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
527 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
528 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
529 priv->clipboard_change_handler))
530 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
531 priv->clipboard_change_handler);
533 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
534 priv->queue_change_handler))
535 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
536 priv->queue_change_handler);
538 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
539 priv->account_removed_handler))
540 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
541 priv->account_removed_handler);
543 if (priv->header_model) {
544 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
545 priv->row_changed_handler))
546 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
547 priv->row_changed_handler);
549 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
550 priv->row_deleted_handler))
551 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
552 priv->row_deleted_handler);
554 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
555 priv->row_inserted_handler))
556 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
557 priv->row_inserted_handler);
559 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
560 priv->rows_reordered_handler))
561 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
562 priv->rows_reordered_handler);
565 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
566 priv->sighandlers = NULL;
568 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
569 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
570 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
572 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
573 MODEST_HEADER_VIEW_OBSERVER(self));
579 modest_msg_view_window_finalize (GObject *obj)
581 ModestMsgViewWindowPrivate *priv;
583 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
585 /* Sanity check: shouldn't be needed, the window mgr should
586 call this function before */
587 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
589 if (priv->other_body != NULL) {
590 g_object_unref (priv->other_body);
591 priv->other_body = NULL;
594 if (priv->header_model != NULL) {
595 g_object_unref (priv->header_model);
596 priv->header_model = NULL;
599 if (priv->remove_attachment_banner) {
600 gtk_widget_destroy (priv->remove_attachment_banner);
601 g_object_unref (priv->remove_attachment_banner);
602 priv->remove_attachment_banner = NULL;
605 if (priv->purge_timeout > 0) {
606 g_source_remove (priv->purge_timeout);
607 priv->purge_timeout = 0;
610 if (priv->row_reference) {
611 gtk_tree_row_reference_free (priv->row_reference);
612 priv->row_reference = NULL;
615 if (priv->next_row_reference) {
616 gtk_tree_row_reference_free (priv->next_row_reference);
617 priv->next_row_reference = NULL;
621 g_free (priv->msg_uid);
622 priv->msg_uid = NULL;
625 G_OBJECT_CLASS(parent_class)->finalize (obj);
629 select_next_valid_row (GtkTreeModel *model,
630 GtkTreeRowReference **row_reference,
634 GtkTreeIter tmp_iter;
636 GtkTreePath *next = NULL;
637 gboolean retval = FALSE, finished;
639 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
641 path = gtk_tree_row_reference_get_path (*row_reference);
642 gtk_tree_model_get_iter (model, &tmp_iter, path);
643 gtk_tree_row_reference_free (*row_reference);
644 *row_reference = NULL;
648 TnyHeader *header = NULL;
650 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
651 gtk_tree_model_get (model, &tmp_iter,
652 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
656 if (msg_is_visible (header, is_outbox)) {
657 next = gtk_tree_model_get_path (model, &tmp_iter);
658 *row_reference = gtk_tree_row_reference_new (model, next);
659 gtk_tree_path_free (next);
663 g_object_unref (header);
666 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
667 next = gtk_tree_model_get_path (model, &tmp_iter);
669 /* Ensure that we are not selecting the same */
670 if (gtk_tree_path_compare (path, next) != 0) {
671 gtk_tree_model_get (model, &tmp_iter,
672 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
675 if (msg_is_visible (header, is_outbox)) {
676 *row_reference = gtk_tree_row_reference_new (model, next);
680 g_object_unref (header);
684 /* If we ended up in the same message
685 then there is no valid next
689 gtk_tree_path_free (next);
691 /* If there are no more messages and we don't
692 want to start again in the first one then
693 there is no valid next message */
699 gtk_tree_path_free (path);
704 /* TODO: This should be in _init(), with the parameters as properties. */
706 modest_msg_view_window_construct (ModestMsgViewWindow *self,
707 const gchar *modest_account_name,
708 const gchar *mailbox,
709 const gchar *msg_uid)
712 ModestMsgViewWindowPrivate *priv = NULL;
713 ModestWindowPrivate *parent_priv = NULL;
714 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
715 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
717 obj = G_OBJECT (self);
718 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
719 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
721 priv->msg_uid = g_strdup (msg_uid);
724 parent_priv->menubar = NULL;
726 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
727 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
730 /* Add common dimming rules */
731 modest_dimming_rules_group_add_rules (toolbar_rules_group,
732 modest_msg_view_toolbar_dimming_entries,
733 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
734 MODEST_WINDOW (self));
735 modest_dimming_rules_group_add_rules (clipboard_rules_group,
736 modest_msg_view_clipboard_dimming_entries,
737 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
738 MODEST_WINDOW (self));
740 /* Insert dimming rules group for this window */
741 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
742 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
743 g_object_unref (toolbar_rules_group);
744 g_object_unref (clipboard_rules_group);
746 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
748 priv->clipboard_change_handler = g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", G_CALLBACK (modest_msg_view_window_clipboard_owner_change), obj);
749 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
750 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
751 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
752 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
753 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
754 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
755 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
756 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
757 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
758 G_CALLBACK (modest_ui_actions_on_details), obj);
759 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
760 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
761 g_signal_connect (G_OBJECT(priv->msg_view), "limit_error",
762 G_CALLBACK (modest_ui_actions_on_limit_error), obj);
763 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
764 G_CALLBACK (on_fetch_image), obj);
766 g_signal_connect (G_OBJECT (obj), "key-release-event",
767 G_CALLBACK (modest_msg_view_window_key_event),
770 g_signal_connect (G_OBJECT (obj), "key-press-event",
771 G_CALLBACK (modest_msg_view_window_key_event),
774 g_signal_connect (G_OBJECT (obj), "move-focus",
775 G_CALLBACK (on_move_focus), obj);
777 g_signal_connect (G_OBJECT (obj), "map-event",
778 G_CALLBACK (_modest_msg_view_window_map_event),
781 /* Mail Operation Queue */
782 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
784 G_CALLBACK (on_queue_changed),
787 /* Account manager */
788 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
790 G_CALLBACK(on_account_removed),
793 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
794 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
796 /* First add out toolbar ... */
797 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
799 /* ... and later the find toolbar. This way find toolbar will
800 be shown over the other */
801 priv->find_toolbar = hildon_find_toolbar_new (NULL);
802 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
803 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
804 g_signal_connect (G_OBJECT (priv->find_toolbar), "close",
805 G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
806 g_signal_connect (G_OBJECT (priv->find_toolbar), "search",
807 G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
808 priv->last_search = NULL;
810 /* Init the clipboard actions dim status */
811 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
813 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
818 /* FIXME: parameter checks */
820 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
821 const gchar *modest_account_name,
822 const gchar *mailbox,
823 const gchar *msg_uid,
825 GtkTreeRowReference *row_reference)
827 ModestMsgViewWindow *window = NULL;
828 ModestMsgViewWindowPrivate *priv = NULL;
829 TnyFolder *header_folder = NULL;
830 ModestHeaderView *header_view = NULL;
831 ModestWindowMgr *mgr = NULL;
834 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
837 mgr = modest_runtime_get_window_mgr ();
838 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
839 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
841 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
843 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
845 /* Remember the message list's TreeModel so we can detect changes
846 * and change the list selection when necessary: */
847 header_folder = modest_header_view_get_folder (header_view);
849 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
850 TNY_FOLDER_TYPE_OUTBOX);
851 priv->header_folder_id = tny_folder_get_id (header_folder);
852 g_object_unref(header_folder);
855 /* Setup row references and connect signals */
856 priv->header_model = g_object_ref (model);
859 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
860 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
861 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
863 priv->row_reference = NULL;
864 priv->next_row_reference = NULL;
867 /* Connect signals */
868 priv->row_changed_handler =
869 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
870 G_CALLBACK(modest_msg_view_window_on_row_changed),
872 priv->row_deleted_handler =
873 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
874 G_CALLBACK(modest_msg_view_window_on_row_deleted),
876 priv->row_inserted_handler =
877 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
878 G_CALLBACK(modest_msg_view_window_on_row_inserted),
880 priv->rows_reordered_handler =
881 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
882 G_CALLBACK(modest_msg_view_window_on_row_reordered),
885 if (header_view != NULL){
886 modest_header_view_add_observer(header_view,
887 MODEST_HEADER_VIEW_OBSERVER(window));
890 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
891 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
892 update_branding (MODEST_MSG_VIEW_WINDOW (window));
894 /* gtk_widget_show_all (GTK_WIDGET (window)); */
895 modest_msg_view_window_update_priority (window);
896 /* Check dimming rules */
897 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
898 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
899 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
901 return MODEST_WINDOW(window);
905 modest_msg_view_window_new_from_uid (const gchar *modest_account_name,
906 const gchar *mailbox,
907 const gchar *msg_uid)
909 ModestMsgViewWindow *window = NULL;
910 ModestMsgViewWindowPrivate *priv = NULL;
911 ModestWindowMgr *mgr = NULL;
913 TnyAccount *account = NULL;
915 mgr = modest_runtime_get_window_mgr ();
916 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
917 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
919 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
921 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
925 is_merge = g_str_has_prefix (msg_uid, "merge:");
927 /* Get the account */
929 account = tny_account_store_find_account (TNY_ACCOUNT_STORE (modest_runtime_get_account_store ()),
933 if (is_merge || account) {
934 TnyFolder *folder = NULL;
936 /* Try to get the message, if it's already downloaded
937 we don't need to connect */
939 folder = modest_tny_folder_store_find_folder_from_uri (TNY_FOLDER_STORE (account), msg_uid);
941 ModestTnyAccountStore *account_store;
942 ModestTnyLocalFoldersAccount *local_folders_account;
944 account_store = modest_runtime_get_account_store ();
945 local_folders_account = MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (
946 modest_tny_account_store_get_local_folders_account (account_store));
947 folder = modest_tny_local_folders_account_get_merged_outbox (local_folders_account);
948 g_object_unref (local_folders_account);
952 gboolean device_online;
954 device = modest_runtime_get_device();
955 device_online = tny_device_is_online (device);
957 message_reader (window, priv, NULL, msg_uid, folder, NULL);
959 TnyMsg *msg = tny_folder_find_msg (folder, msg_uid, NULL);
961 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
962 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
963 update_branding (MODEST_MSG_VIEW_WINDOW (window));
964 g_object_unref (msg);
966 message_reader (window, priv, NULL, msg_uid, folder, NULL);
969 g_object_unref (folder);
974 /* Check dimming rules */
975 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
976 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
977 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
979 return MODEST_WINDOW(window);
983 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
984 const gchar *modest_account_name,
985 const gchar *mailbox,
986 const gchar *msg_uid,
987 GtkTreeRowReference *row_reference)
989 ModestMsgViewWindow *window = NULL;
990 ModestMsgViewWindowPrivate *priv = NULL;
991 TnyFolder *header_folder = NULL;
992 ModestWindowMgr *mgr = NULL;
996 mgr = modest_runtime_get_window_mgr ();
997 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
998 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1000 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1002 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1004 /* Remember the message list's TreeModel so we can detect changes
1005 * and change the list selection when necessary: */
1007 if (header_view != NULL){
1008 header_folder = modest_header_view_get_folder(header_view);
1009 /* This could happen if the header folder was
1010 unseleted before opening this msg window (for
1011 example if the user selects an account in the
1012 folder view of the main window */
1013 if (header_folder) {
1014 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) ==
1015 TNY_FOLDER_TYPE_OUTBOX);
1016 priv->header_folder_id = tny_folder_get_id(header_folder);
1017 g_object_unref(header_folder);
1021 /* Setup row references and connect signals */
1022 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1023 g_object_ref (priv->header_model);
1025 if (row_reference) {
1026 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1027 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1028 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1030 priv->row_reference = NULL;
1031 priv->next_row_reference = NULL;
1034 /* Connect signals */
1035 priv->row_changed_handler =
1036 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1037 G_CALLBACK(modest_msg_view_window_on_row_changed),
1039 priv->row_deleted_handler =
1040 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1041 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1043 priv->row_inserted_handler =
1044 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1045 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1047 priv->rows_reordered_handler =
1048 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1049 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1052 if (header_view != NULL){
1053 modest_header_view_add_observer(header_view,
1054 MODEST_HEADER_VIEW_OBSERVER(window));
1057 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1058 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1060 path = gtk_tree_row_reference_get_path (row_reference);
1061 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1063 gtk_tree_model_get (priv->header_model, &iter,
1064 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1066 message_reader (window, priv, header, NULL, NULL, row_reference);
1067 g_object_unref (header);
1069 gtk_tree_path_free (path);
1071 /* Check dimming rules */
1072 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1073 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1074 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1076 return MODEST_WINDOW(window);
1080 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1081 const gchar *modest_account_name,
1082 const gchar *mailbox,
1083 const gchar *msg_uid)
1085 ModestMsgViewWindow *window = NULL;
1086 ModestMsgViewWindowPrivate *priv = NULL;
1087 ModestWindowMgr *mgr = NULL;
1089 mgr = modest_runtime_get_window_mgr ();
1090 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1091 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1092 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1094 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1096 /* Remember that this is a search result,
1097 * so we can disable some UI appropriately: */
1098 priv->is_search_result = TRUE;
1100 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1101 update_branding (MODEST_MSG_VIEW_WINDOW (window));
1103 update_window_title (window);
1104 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1105 modest_msg_view_window_update_priority (window);
1107 /* Check dimming rules */
1108 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1109 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1110 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1112 return MODEST_WINDOW(window);
1116 modest_msg_view_window_is_other_body (ModestMsgViewWindow *self)
1118 ModestMsgViewWindowPrivate *priv = NULL;
1120 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1121 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1123 return (priv->other_body != NULL);
1127 modest_msg_view_window_new_with_other_body (TnyMsg *msg,
1128 TnyMimePart *other_body,
1129 const gchar *modest_account_name,
1130 const gchar *mailbox,
1131 const gchar *msg_uid)
1133 GObject *obj = NULL;
1134 ModestMsgViewWindowPrivate *priv;
1135 ModestWindowMgr *mgr = NULL;
1137 g_return_val_if_fail (msg, NULL);
1138 mgr = modest_runtime_get_window_mgr ();
1139 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1140 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1141 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1142 modest_account_name, mailbox, msg_uid);
1145 priv->other_body = g_object_ref (other_body);
1146 modest_msg_view_set_msg_with_other_body (MODEST_MSG_VIEW (priv->msg_view), msg, other_body);
1148 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1150 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1151 update_branding (MODEST_MSG_VIEW_WINDOW (obj));
1153 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1155 /* Check dimming rules */
1156 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1157 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1158 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1160 return MODEST_WINDOW(obj);
1164 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1165 const gchar *modest_account_name,
1166 const gchar *mailbox,
1167 const gchar *msg_uid)
1169 return modest_msg_view_window_new_with_other_body (msg, NULL, modest_account_name, mailbox, msg_uid);
1173 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1176 ModestMsgViewWindow *window)
1178 check_dimming_rules_after_change (window);
1182 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1184 ModestMsgViewWindow *window)
1186 check_dimming_rules_after_change (window);
1188 /* The window could have dissapeared */
1191 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1193 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1194 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1198 /* On insertions we check if the folder still has the message we are
1199 * showing or do not. If do not, we do nothing. Which means we are still
1200 * not attached to any header folder and thus next/prev buttons are
1201 * still dimmed. Once the message that is shown by msg-view is found, the
1202 * new model of header-view will be attached and the references will be set.
1203 * On each further insertions dimming rules will be checked. However
1204 * this requires extra CPU time at least works.
1205 * (An message might be deleted from TnyFolder and thus will not be
1206 * inserted into the model again for example if it is removed by the
1207 * imap server and the header view is refreshed.)
1210 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1211 GtkTreePath *tree_path,
1212 GtkTreeIter *tree_iter,
1213 ModestMsgViewWindow *window)
1215 ModestMsgViewWindowPrivate *priv = NULL;
1216 TnyHeader *header = NULL;
1218 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1219 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1221 g_assert (model == priv->header_model);
1223 /* Check if the newly inserted message is the same we are actually
1224 * showing. IF not, we should remain detached from the header model
1225 * and thus prev and next toolbar buttons should remain dimmed. */
1226 gtk_tree_model_get (model, tree_iter,
1227 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1230 if (TNY_IS_HEADER (header)) {
1233 uid = modest_tny_folder_get_header_unique_id (header);
1234 if (!g_str_equal(priv->msg_uid, uid)) {
1235 check_dimming_rules_after_change (window);
1237 g_object_unref (G_OBJECT(header));
1241 g_object_unref(G_OBJECT(header));
1244 if (priv->row_reference) {
1245 gtk_tree_row_reference_free (priv->row_reference);
1248 /* Setup row_reference for the actual msg. */
1249 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1250 if (priv->row_reference == NULL) {
1251 g_warning("%s: No reference for msg header item.", __FUNCTION__);
1255 /* Now set up next_row_reference. */
1256 if (priv->next_row_reference) {
1257 gtk_tree_row_reference_free (priv->next_row_reference);
1260 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1261 select_next_valid_row (priv->header_model,
1262 &(priv->next_row_reference), FALSE, priv->is_outbox);
1264 /* Connect the remaining callbacks to become able to detect
1265 * changes in header-view. */
1266 priv->row_changed_handler =
1267 g_signal_connect (priv->header_model, "row-changed",
1268 G_CALLBACK (modest_msg_view_window_on_row_changed),
1270 priv->row_deleted_handler =
1271 g_signal_connect (priv->header_model, "row-deleted",
1272 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1274 priv->rows_reordered_handler =
1275 g_signal_connect (priv->header_model, "rows-reordered",
1276 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1279 check_dimming_rules_after_change (window);
1283 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1287 ModestMsgViewWindow *window)
1289 ModestMsgViewWindowPrivate *priv = NULL;
1290 gboolean already_changed = FALSE;
1292 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1294 /* If the current row was reordered select the proper next
1295 valid row. The same if the next row reference changes */
1296 if (!priv->row_reference ||
1297 !gtk_tree_row_reference_valid (priv->row_reference))
1300 if (priv->next_row_reference &&
1301 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1302 GtkTreePath *cur, *next;
1303 /* Check that the order is still the correct one */
1304 cur = gtk_tree_row_reference_get_path (priv->row_reference);
1305 next = gtk_tree_row_reference_get_path (priv->next_row_reference);
1306 gtk_tree_path_next (cur);
1307 if (gtk_tree_path_compare (cur, next) != 0) {
1308 gtk_tree_row_reference_free (priv->next_row_reference);
1309 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1310 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1311 already_changed = TRUE;
1313 gtk_tree_path_free (cur);
1314 gtk_tree_path_free (next);
1316 if (priv->next_row_reference)
1317 gtk_tree_row_reference_free (priv->next_row_reference);
1318 /* Update next row reference */
1319 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1320 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1321 already_changed = TRUE;
1324 check_dimming_rules_after_change (window);
1327 /* The modest_msg_view_window_update_model_replaced implements update
1328 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1329 * actually belongs to the header-view is the same as the TnyFolder of
1330 * the message of msg-view or not. If they are different, there is
1331 * nothing to do. If they are the same, then the model has replaced and
1332 * the reference in msg-view shall be replaced from the old model to
1333 * the new model. In this case the view will be detached from it's
1334 * header folder. From this point the next/prev buttons are dimmed.
1337 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1338 GtkTreeModel *model,
1339 const gchar *tny_folder_id)
1341 ModestMsgViewWindowPrivate *priv = NULL;
1342 ModestMsgViewWindow *window = NULL;
1344 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1345 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1347 window = MODEST_MSG_VIEW_WINDOW(observer);
1348 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1350 /* If there is an other folder in the header-view then we do
1351 * not care about it's model (msg list). Else if the
1352 * header-view shows the folder the msg shown by us is in, we
1353 * shall replace our model reference and make some check. */
1354 if(model == NULL || tny_folder_id == NULL ||
1355 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1358 /* Model is changed(replaced), so we should forget the old
1359 * one. Because there might be other references and there
1360 * might be some change on the model even if we unreferenced
1361 * it, we need to disconnect our signals here. */
1362 if (priv->header_model) {
1363 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1364 priv->row_changed_handler))
1365 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1366 priv->row_changed_handler);
1367 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1368 priv->row_deleted_handler))
1369 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1370 priv->row_deleted_handler);
1371 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1372 priv->row_inserted_handler))
1373 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1374 priv->row_inserted_handler);
1375 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1376 priv->rows_reordered_handler))
1377 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1378 priv->rows_reordered_handler);
1381 if (priv->row_reference)
1382 gtk_tree_row_reference_free (priv->row_reference);
1383 if (priv->next_row_reference)
1384 gtk_tree_row_reference_free (priv->next_row_reference);
1385 g_object_unref(priv->header_model);
1388 priv->row_changed_handler = 0;
1389 priv->row_deleted_handler = 0;
1390 priv->row_inserted_handler = 0;
1391 priv->rows_reordered_handler = 0;
1392 priv->next_row_reference = NULL;
1393 priv->row_reference = NULL;
1394 priv->header_model = NULL;
1397 priv->header_model = g_object_ref (model);
1399 /* Also we must connect to the new model for row insertions.
1400 * Only for insertions now. We will need other ones only after
1401 * the msg is show by msg-view is added to the new model. */
1402 priv->row_inserted_handler =
1403 g_signal_connect (priv->header_model, "row-inserted",
1404 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1407 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1408 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1412 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1414 ModestMsgViewWindowPrivate *priv= NULL;
1416 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1417 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1419 return priv->progress_hint;
1423 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1425 ModestMsgViewWindowPrivate *priv= NULL;
1427 TnyHeader *header = NULL;
1428 GtkTreePath *path = NULL;
1431 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1432 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1434 /* If the message was not obtained from a treemodel,
1435 * for instance if it was opened directly by the search UI:
1437 if (priv->header_model == NULL ||
1438 priv->row_reference == NULL ||
1439 !gtk_tree_row_reference_valid (priv->row_reference)) {
1440 msg = modest_msg_view_window_get_message (self);
1442 header = tny_msg_get_header (msg);
1443 g_object_unref (msg);
1448 /* Get iter of the currently selected message in the header view: */
1449 path = gtk_tree_row_reference_get_path (priv->row_reference);
1450 g_return_val_if_fail (path != NULL, NULL);
1451 gtk_tree_model_get_iter (priv->header_model,
1455 /* Get current message header */
1456 gtk_tree_model_get (priv->header_model, &iter,
1457 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1460 gtk_tree_path_free (path);
1465 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1467 ModestMsgViewWindowPrivate *priv;
1469 g_return_val_if_fail (self, NULL);
1471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1473 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1477 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1479 ModestMsgViewWindowPrivate *priv;
1481 g_return_val_if_fail (self, NULL);
1483 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1485 return (const gchar*) priv->msg_uid;
1488 /* Used for the Ctrl+F accelerator */
1490 modest_msg_view_window_toggle_find_toolbar (GtkWidget *obj,
1493 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1494 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1496 if (GTK_WIDGET_VISIBLE (priv->find_toolbar)) {
1497 modest_msg_view_window_find_toolbar_close (obj, data);
1499 modest_msg_view_window_show_find_toolbar (obj, data);
1503 /* Handler for menu option */
1505 modest_msg_view_window_show_find_toolbar (GtkWidget *obj,
1508 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1509 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1511 gtk_widget_show (priv->find_toolbar);
1512 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1515 /* Handler for click on the "X" close button in find toolbar */
1517 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1518 ModestMsgViewWindow *obj)
1520 ModestMsgViewWindowPrivate *priv;
1522 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1525 gtk_widget_hide (priv->find_toolbar);
1526 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1530 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1531 ModestMsgViewWindow *obj)
1533 gchar *current_search;
1534 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1536 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1537 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1541 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1543 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1544 g_free (current_search);
1545 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1549 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1551 g_free (priv->last_search);
1552 priv->last_search = g_strdup (current_search);
1553 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1556 hildon_banner_show_information (NULL, NULL,
1557 _HL("ckct_ib_find_no_matches"));
1558 g_free (priv->last_search);
1559 priv->last_search = NULL;
1561 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1564 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1565 hildon_banner_show_information (NULL, NULL,
1566 _HL("ckct_ib_find_search_complete"));
1567 g_free (priv->last_search);
1568 priv->last_search = NULL;
1570 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1574 g_free (current_search);
1579 modest_msg_view_window_set_zoom (ModestWindow *window,
1582 ModestMsgViewWindowPrivate *priv;
1583 ModestWindowPrivate *parent_priv;
1585 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1587 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1588 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1589 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1594 modest_msg_view_window_get_zoom (ModestWindow *window)
1596 ModestMsgViewWindowPrivate *priv;
1598 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1600 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1601 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1605 modest_msg_view_window_zoom_plus (ModestWindow *window)
1608 ModestMsgViewWindowPrivate *priv;
1612 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1613 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1615 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1617 if (zoom_level >= 2.0) {
1618 hildon_banner_show_information (NULL, NULL,
1619 _CS("ckct_ib_max_zoom_level_reached"));
1621 } else if (zoom_level >= 1.5) {
1623 } else if (zoom_level >= 1.2) {
1625 } else if (zoom_level >= 1.0) {
1627 } else if (zoom_level >= 0.8) {
1629 } else if (zoom_level >= 0.5) {
1635 /* set zoom level */
1636 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1637 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1638 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1639 g_free (banner_text);
1640 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1646 modest_msg_view_window_zoom_minus (ModestWindow *window)
1649 ModestMsgViewWindowPrivate *priv;
1653 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1654 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1656 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1658 if (zoom_level <= 0.5) {
1659 hildon_banner_show_information (NULL, NULL,
1660 _CS("ckct_ib_min_zoom_level_reached"));
1662 } else if (zoom_level <= 0.8) {
1664 } else if (zoom_level <= 1.0) {
1666 } else if (zoom_level <= 1.2) {
1668 } else if (zoom_level <= 1.5) {
1670 } else if (zoom_level <= 2.0) {
1676 /* set zoom level */
1677 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1678 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1679 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1680 g_free (banner_text);
1681 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1688 modest_msg_view_window_key_event (GtkWidget *window,
1694 focus = gtk_window_get_focus (GTK_WINDOW (window));
1696 /* for the find toolbar case */
1697 if (focus && GTK_IS_ENTRY (focus)) {
1698 if (event->keyval == GDK_BackSpace) {
1700 copy = gdk_event_copy ((GdkEvent *) event);
1701 gtk_widget_event (focus, copy);
1702 gdk_event_free (copy);
1707 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1708 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1709 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1710 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1711 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1712 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1713 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1714 /* gboolean return_value; */
1716 if (event->type == GDK_KEY_PRESS) {
1717 GtkScrollType scroll_type;
1719 switch (event->keyval) {
1722 scroll_type = GTK_SCROLL_STEP_UP; break;
1725 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1727 case GDK_KP_Page_Up:
1728 scroll_type = GTK_SCROLL_PAGE_UP; break;
1730 case GDK_KP_Page_Down:
1731 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1734 scroll_type = GTK_SCROLL_START; break;
1737 scroll_type = GTK_SCROLL_END; break;
1738 default: scroll_type = GTK_SCROLL_NONE;
1741 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1742 /* scroll_type, FALSE, &return_value); */
1753 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1756 ModestMsgViewWindowPrivate *priv;
1757 GtkTreeIter tmp_iter;
1758 gboolean is_last_selected;
1760 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1761 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1763 /*if no model (so no rows at all), then virtually we are the last*/
1764 if (!priv->header_model || !priv->row_reference)
1767 if (!gtk_tree_row_reference_valid (priv->row_reference))
1770 path = gtk_tree_row_reference_get_path (priv->row_reference);
1774 is_last_selected = TRUE;
1775 while (is_last_selected) {
1777 gtk_tree_path_next (path);
1778 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1780 gtk_tree_model_get (priv->header_model, &tmp_iter,
1781 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1784 if (msg_is_visible (header, priv->is_outbox))
1785 is_last_selected = FALSE;
1786 g_object_unref(G_OBJECT(header));
1789 gtk_tree_path_free (path);
1790 return is_last_selected;
1794 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1796 ModestMsgViewWindowPrivate *priv;
1798 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1799 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1801 return priv->header_model != NULL;
1805 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1807 ModestMsgViewWindowPrivate *priv;
1809 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1810 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1812 return priv->is_search_result;
1816 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1818 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1820 if (!check_outbox) {
1823 ModestTnySendQueueStatus status;
1824 status = modest_tny_all_send_queues_get_msg_status (header);
1825 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1826 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1831 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1834 ModestMsgViewWindowPrivate *priv;
1835 gboolean is_first_selected;
1836 GtkTreeIter tmp_iter;
1838 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1839 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1841 /*if no model (so no rows at all), then virtually we are the first*/
1842 if (!priv->header_model || !priv->row_reference)
1845 if (!gtk_tree_row_reference_valid (priv->row_reference))
1848 path = gtk_tree_row_reference_get_path (priv->row_reference);
1852 is_first_selected = TRUE;
1853 while (is_first_selected) {
1855 if(!gtk_tree_path_prev (path))
1857 /* Here the 'if' is needless for logic, but let make sure
1858 * iter is valid for gtk_tree_model_get. */
1859 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1861 gtk_tree_model_get (priv->header_model, &tmp_iter,
1862 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1865 if (msg_is_visible (header, priv->is_outbox))
1866 is_first_selected = FALSE;
1867 g_object_unref(G_OBJECT(header));
1870 gtk_tree_path_free (path);
1871 return is_first_selected;
1878 GtkTreeRowReference *row_reference;
1882 message_reader_performer (gboolean canceled,
1884 GtkWindow *parent_window,
1885 TnyAccount *account,
1888 ModestMailOperation *mail_op = NULL;
1889 MsgReaderInfo *info;
1891 info = (MsgReaderInfo *) user_data;
1892 if (canceled || err) {
1893 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1897 /* Register the header - it'll be unregistered in the callback */
1899 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1901 /* New mail operation */
1902 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1903 modest_ui_actions_disk_operations_error_handler,
1906 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1908 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1910 modest_mail_operation_find_msg (mail_op, info->folder, info->msg_uid, TRUE, view_msg_cb, NULL);
1911 g_object_unref (mail_op);
1913 /* Update dimming rules */
1914 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1915 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1918 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1919 g_free (info->msg_uid);
1921 g_object_unref (info->folder);
1923 g_object_unref (info->header);
1924 g_slice_free (MsgReaderInfo, info);
1929 * Reads the message whose summary item is @header. It takes care of
1930 * several things, among others:
1932 * If the message was not previously downloaded then ask the user
1933 * before downloading. If there is no connection launch the connection
1934 * dialog. Update toolbar dimming rules.
1936 * Returns: TRUE if the mail operation was started, otherwise if the
1937 * user do not want to download the message, or if the user do not
1938 * want to connect, then the operation is not issued
1941 message_reader (ModestMsgViewWindow *window,
1942 ModestMsgViewWindowPrivate *priv,
1944 const gchar *msg_uid,
1946 GtkTreeRowReference *row_reference)
1948 ModestWindowMgr *mgr;
1949 TnyAccount *account;
1950 MsgReaderInfo *info;
1952 /* We set the header from model while we're loading */
1953 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1954 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1960 g_object_ref (folder);
1962 mgr = modest_runtime_get_window_mgr ();
1963 /* Msg download completed */
1964 if (!header || !(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1966 /* Ask the user if he wants to download the message if
1968 if (!tny_device_is_online (modest_runtime_get_device())) {
1969 GtkResponseType response;
1971 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1972 _("mcen_nc_get_msg"));
1973 if (response == GTK_RESPONSE_CANCEL) {
1974 update_window_title (window);
1979 folder = tny_header_get_folder (header);
1981 info = g_slice_new (MsgReaderInfo);
1982 info->msg_uid = g_strdup (msg_uid);
1984 info->header = g_object_ref (header);
1986 info->header = NULL;
1988 info->folder = g_object_ref (folder);
1990 info->folder = NULL;
1991 if (row_reference) {
1992 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1994 info->row_reference = NULL;
1997 /* Offer the connection dialog if necessary */
1998 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
2000 TNY_FOLDER_STORE (folder),
2001 message_reader_performer,
2003 g_object_unref (folder);
2009 folder = tny_header_get_folder (header);
2011 account = tny_folder_get_account (folder);
2012 info = g_slice_new (MsgReaderInfo);
2013 info->msg_uid = g_strdup (msg_uid);
2015 info->folder = g_object_ref (folder);
2017 info->folder = NULL;
2019 info->header = g_object_ref (header);
2021 info->header = NULL;
2023 info->row_reference = gtk_tree_row_reference_copy (row_reference);
2025 info->row_reference = NULL;
2027 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
2028 g_object_unref (account);
2029 g_object_unref (folder);
2035 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2037 ModestMsgViewWindowPrivate *priv;
2038 GtkTreePath *path= NULL;
2039 GtkTreeIter tmp_iter;
2041 gboolean retval = TRUE;
2042 GtkTreeRowReference *row_reference = NULL;
2044 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2045 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2047 if (!priv->row_reference)
2050 /* Update the next row reference if it's not valid. This could
2051 happen if for example the header which it was pointing to,
2052 was deleted. The best place to do it is in the row-deleted
2053 handler but the tinymail model do not work like the glib
2054 tree models and reports the deletion when the row is still
2056 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2057 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2058 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2059 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2062 if (priv->next_row_reference)
2063 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2067 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2069 gtk_tree_model_get_iter (priv->header_model,
2072 gtk_tree_path_free (path);
2074 gtk_tree_model_get (priv->header_model, &tmp_iter,
2075 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2078 /* Read the message & show it */
2079 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2082 gtk_tree_row_reference_free (row_reference);
2085 g_object_unref (header);
2091 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2093 ModestMsgViewWindowPrivate *priv = NULL;
2095 gboolean finished = FALSE;
2096 gboolean retval = FALSE;
2098 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2099 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2101 /* Return inmediatly if there is no header model */
2102 if (!priv->header_model || !priv->row_reference)
2105 path = gtk_tree_row_reference_get_path (priv->row_reference);
2106 while (!finished && gtk_tree_path_prev (path)) {
2110 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2111 gtk_tree_model_get (priv->header_model, &iter,
2112 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2116 if (msg_is_visible (header, priv->is_outbox)) {
2117 GtkTreeRowReference *row_reference;
2118 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2119 /* Read the message & show it */
2120 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2121 gtk_tree_row_reference_free (row_reference);
2125 g_object_unref (header);
2129 gtk_tree_path_free (path);
2134 view_msg_cb (ModestMailOperation *mail_op,
2141 ModestMsgViewWindow *self = NULL;
2142 ModestMsgViewWindowPrivate *priv = NULL;
2143 GtkTreeRowReference *row_reference = NULL;
2145 /* Unregister the header (it was registered before creating the mail operation) */
2146 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2148 row_reference = (GtkTreeRowReference *) user_data;
2151 gtk_tree_row_reference_free (row_reference);
2152 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2154 /* Restore window title */
2155 update_window_title (self);
2156 g_object_unref (self);
2161 /* If there was any error */
2162 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2164 gtk_tree_row_reference_free (row_reference);
2165 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2167 /* Restore window title */
2168 update_window_title (self);
2169 g_object_unref (self);
2174 /* Get the window */
2175 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2176 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2177 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2179 /* Update the row reference */
2180 if (priv->row_reference != NULL) {
2181 gtk_tree_row_reference_free (priv->row_reference);
2182 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2183 if (priv->next_row_reference != NULL) {
2184 gtk_tree_row_reference_free (priv->next_row_reference);
2186 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2187 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2190 /* Mark header as read */
2191 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2192 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2194 /* Set new message */
2195 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2196 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2197 modest_msg_view_window_update_priority (self);
2198 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2199 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2200 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2203 /* Set the new message uid of the window */
2204 if (priv->msg_uid) {
2205 g_free (priv->msg_uid);
2206 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2209 /* Notify the observers */
2210 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2211 0, priv->header_model, priv->row_reference);
2214 g_object_unref (self);
2216 gtk_tree_row_reference_free (row_reference);
2220 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2222 ModestMsgViewWindowPrivate *priv;
2224 TnyFolderType folder_type;
2226 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2228 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2230 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2234 folder = tny_msg_get_folder (msg);
2236 folder_type = modest_tny_folder_guess_folder_type (folder);
2237 g_object_unref (folder);
2239 g_object_unref (msg);
2247 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2249 ModestMsgViewWindowPrivate *priv;
2250 TnyHeader *header = NULL;
2251 TnyHeaderFlags flags = 0;
2253 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2255 if (priv->header_model && priv->row_reference) {
2257 GtkTreePath *path = NULL;
2259 path = gtk_tree_row_reference_get_path (priv->row_reference);
2260 g_return_if_fail (path != NULL);
2261 gtk_tree_model_get_iter (priv->header_model,
2263 gtk_tree_row_reference_get_path (priv->row_reference));
2265 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2267 gtk_tree_path_free (path);
2270 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2272 header = tny_msg_get_header (msg);
2273 g_object_unref (msg);
2278 flags = tny_header_get_flags (header);
2279 g_object_unref(G_OBJECT(header));
2282 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2287 toolbar_resize (ModestMsgViewWindow *self)
2289 ModestMsgViewWindowPrivate *priv = NULL;
2290 ModestWindowPrivate *parent_priv = NULL;
2292 gint static_button_size;
2293 ModestWindowMgr *mgr;
2295 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2296 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2297 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2299 mgr = modest_runtime_get_window_mgr ();
2300 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2302 if (parent_priv->toolbar) {
2303 /* left size buttons */
2304 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2305 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2306 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2307 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2308 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2309 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2310 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2311 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2312 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2313 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2314 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2315 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2316 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2317 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2318 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2319 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2321 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2322 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2323 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2324 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2329 modest_msg_view_window_show_toolbar (ModestWindow *self,
2330 gboolean show_toolbar)
2332 ModestMsgViewWindowPrivate *priv = NULL;
2333 ModestWindowPrivate *parent_priv;
2335 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2336 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2338 /* Set optimized view status */
2339 priv->optimized_view = !show_toolbar;
2341 if (!parent_priv->toolbar) {
2342 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2344 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2345 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2347 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2348 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2349 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2352 hildon_window_add_toolbar (HILDON_WINDOW (self),
2353 GTK_TOOLBAR (parent_priv->toolbar));
2358 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2359 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2360 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2362 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2363 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2364 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2366 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2369 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2370 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2375 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2377 ModestMsgViewWindow *window)
2379 if (!GTK_WIDGET_VISIBLE (window))
2382 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2386 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2388 ModestMsgViewWindowPrivate *priv;
2390 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2391 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2393 return priv->progress_hint;
2397 observers_empty (ModestMsgViewWindow *self)
2400 ModestMsgViewWindowPrivate *priv;
2401 gboolean is_empty = TRUE;
2402 guint pending_ops = 0;
2404 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2405 tmp = priv->progress_widgets;
2407 /* Check all observers */
2408 while (tmp && is_empty) {
2409 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2410 is_empty = pending_ops == 0;
2412 tmp = g_slist_next(tmp);
2419 on_account_removed (TnyAccountStore *account_store,
2420 TnyAccount *account,
2423 /* Do nothing if it's a transport account, because we only
2424 show the messages of a store account */
2425 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2426 const gchar *parent_acc = NULL;
2427 const gchar *our_acc = NULL;
2429 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2430 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2432 /* Close this window if I'm showing a message of the removed account */
2433 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2434 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2439 on_mail_operation_started (ModestMailOperation *mail_op,
2442 ModestMsgViewWindow *self;
2443 ModestMailOperationTypeOperation op_type;
2445 ModestMsgViewWindowPrivate *priv;
2446 GObject *source = NULL;
2448 self = MODEST_MSG_VIEW_WINDOW (user_data);
2449 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2450 op_type = modest_mail_operation_get_type_operation (mail_op);
2451 tmp = priv->progress_widgets;
2452 source = modest_mail_operation_get_source(mail_op);
2453 if (G_OBJECT (self) == source) {
2454 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2455 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2456 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2457 set_progress_hint (self, TRUE);
2459 modest_progress_object_add_operation (
2460 MODEST_PROGRESS_OBJECT (tmp->data),
2462 tmp = g_slist_next (tmp);
2466 g_object_unref (source);
2468 /* Update dimming rules */
2469 check_dimming_rules_after_change (self);
2473 on_mail_operation_finished (ModestMailOperation *mail_op,
2476 ModestMsgViewWindow *self;
2477 ModestMailOperationTypeOperation op_type;
2479 ModestMsgViewWindowPrivate *priv;
2481 self = MODEST_MSG_VIEW_WINDOW (user_data);
2482 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2483 op_type = modest_mail_operation_get_type_operation (mail_op);
2484 tmp = priv->progress_widgets;
2486 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2487 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2488 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2490 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2492 tmp = g_slist_next (tmp);
2495 /* If no more operations are being observed, NORMAL mode is enabled again */
2496 if (observers_empty (self)) {
2497 set_progress_hint (self, FALSE);
2501 /* Update dimming rules. We have to do this right here
2502 and not in view_msg_cb because at that point the
2503 transfer mode is still enabled so the dimming rule
2504 won't let the user delete the message that has been
2505 readed for example */
2506 check_dimming_rules_after_change (self);
2510 on_queue_changed (ModestMailOperationQueue *queue,
2511 ModestMailOperation *mail_op,
2512 ModestMailOperationQueueNotification type,
2513 ModestMsgViewWindow *self)
2515 ModestMsgViewWindowPrivate *priv;
2517 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2519 /* If this operations was created by another window, do nothing */
2520 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2523 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2524 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2526 "operation-started",
2527 G_CALLBACK (on_mail_operation_started),
2529 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2531 "operation-finished",
2532 G_CALLBACK (on_mail_operation_finished),
2534 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2535 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2537 "operation-started");
2538 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2540 "operation-finished");
2545 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2547 ModestMsgViewWindowPrivate *priv;
2548 TnyList *selected_attachments = NULL;
2550 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2551 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2553 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2554 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2556 return selected_attachments;
2560 ModestMsgViewWindow *self;
2562 } DecodeAsyncHelper;
2565 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2571 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2573 /* It could happen that the window was closed */
2574 if (GTK_WIDGET_VISIBLE (helper->self))
2575 set_progress_hint (helper->self, FALSE);
2577 if (cancelled || err) {
2579 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2580 modest_platform_information_banner (NULL, NULL, msg);
2586 /* make the file read-only */
2587 g_chmod(helper->file_path, 0444);
2589 /* Activate the file */
2590 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2594 g_object_unref (helper->self);
2595 g_free (helper->file_path);
2596 g_slice_free (DecodeAsyncHelper, helper);
2600 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2601 TnyMimePart *mime_part)
2603 ModestMsgViewWindowPrivate *priv;
2604 const gchar *msg_uid;
2605 gchar *attachment_uid = NULL;
2606 gint attachment_index = 0;
2607 TnyList *attachments;
2609 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2610 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2611 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2613 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2614 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2615 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2616 g_object_unref (attachments);
2618 if (msg_uid && attachment_index >= 0) {
2619 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2622 if (mime_part == NULL) {
2623 gboolean error = FALSE;
2624 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2625 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2627 } else if (tny_list_get_length (selected_attachments) > 1) {
2628 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2632 iter = tny_list_create_iterator (selected_attachments);
2633 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2634 g_object_unref (iter);
2636 if (selected_attachments)
2637 g_object_unref (selected_attachments);
2642 g_object_ref (mime_part);
2645 if (tny_mime_part_is_purged (mime_part))
2648 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2649 gchar *filepath = NULL;
2650 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2651 gboolean show_error_banner = FALSE;
2652 TnyFsStream *temp_stream = NULL;
2653 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2656 if (temp_stream != NULL) {
2657 DecodeAsyncHelper *helper;
2659 /* Activate progress hint */
2660 set_progress_hint (window, TRUE);
2662 helper = g_slice_new0 (DecodeAsyncHelper);
2663 helper->self = g_object_ref (window);
2664 helper->file_path = g_strdup (filepath);
2666 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2667 on_decode_to_stream_async_handler,
2670 g_object_unref (temp_stream);
2671 /* NOTE: files in the temporary area will be automatically
2672 * cleaned after some time if they are no longer in use */
2675 const gchar *content_type;
2676 /* the file may already exist but it isn't writable,
2677 * let's try to open it anyway */
2678 content_type = tny_mime_part_get_content_type (mime_part);
2679 modest_platform_activate_file (filepath, content_type);
2681 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2682 show_error_banner = TRUE;
2687 if (show_error_banner)
2688 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2689 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2690 ModestWindowMgr *mgr;
2691 ModestWindow *msg_win = NULL;
2692 TnyMsg *current_msg;
2696 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2697 mgr = modest_runtime_get_window_mgr ();
2698 header = tny_msg_get_header (TNY_MSG (current_msg));
2699 found = modest_window_mgr_find_registered_message_uid (mgr,
2704 g_debug ("window for this body is already being created");
2707 /* it's not found, so create a new window for it */
2708 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2709 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2710 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2712 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2714 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2715 account, mailbox, attachment_uid);
2717 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2718 modest_window_get_zoom (MODEST_WINDOW (window)));
2719 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2720 gtk_widget_show_all (GTK_WIDGET (msg_win));
2722 gtk_widget_destroy (GTK_WIDGET (msg_win));
2724 g_object_unref (current_msg);
2726 /* message attachment */
2727 TnyHeader *header = NULL;
2728 ModestWindowMgr *mgr;
2729 ModestWindow *msg_win = NULL;
2732 header = tny_msg_get_header (TNY_MSG (mime_part));
2733 mgr = modest_runtime_get_window_mgr ();
2734 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2737 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2738 * thus, we don't do anything */
2739 g_debug ("window for is already being created");
2741 /* it's not found, so create a new window for it */
2742 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2743 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2744 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2746 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2747 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2748 mailbox, attachment_uid);
2749 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2750 modest_window_get_zoom (MODEST_WINDOW (window)));
2751 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2752 gtk_widget_show_all (GTK_WIDGET (msg_win));
2754 gtk_widget_destroy (GTK_WIDGET (msg_win));
2760 g_free (attachment_uid);
2762 g_object_unref (mime_part);
2774 GnomeVFSResult result;
2778 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2779 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2780 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2781 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2784 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2788 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2789 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2790 g_free (pair->filename);
2791 g_object_unref (pair->part);
2792 g_slice_free (SaveMimePartPair, pair);
2794 g_list_free (info->pairs);
2798 g_slice_free (SaveMimePartInfo, info);
2803 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2805 /* This is a GDK lock because we are an idle callback and
2806 * hildon_banner_show_information is or does Gtk+ code */
2808 gdk_threads_enter (); /* CHECKED */
2809 if (info->result == GNOME_VFS_OK) {
2810 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2811 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2814 /* Check if the uri belongs to the external mmc */
2815 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2816 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2818 msg = g_strdup (_KR("cerm_memory_card_full"));
2819 modest_platform_information_banner (NULL, NULL, msg);
2822 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2824 save_mime_part_info_free (info, FALSE);
2825 gdk_threads_leave (); /* CHECKED */
2831 save_mime_part_to_file (SaveMimePartInfo *info)
2833 GnomeVFSHandle *handle;
2835 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2837 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2838 if (info->result == GNOME_VFS_OK) {
2839 GError *error = NULL;
2840 stream = tny_vfs_stream_new (handle);
2841 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2842 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2844 if ((error->domain == TNY_ERROR_DOMAIN) &&
2845 (error->code == TNY_IO_ERROR_WRITE) &&
2846 (errno == ENOSPC)) {
2847 info->result = GNOME_VFS_ERROR_NO_SPACE;
2849 info->result = GNOME_VFS_ERROR_IO;
2852 g_object_unref (G_OBJECT (stream));
2854 g_warning ("Could not create save attachment %s: %s\n",
2855 pair->filename, gnome_vfs_result_to_string (info->result));
2858 /* Go on saving remaining files */
2859 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2860 if (info->pairs != NULL) {
2861 save_mime_part_to_file (info);
2863 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2870 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2871 SaveMimePartInfo *info)
2873 gboolean is_ok = TRUE;
2874 gint replaced_files = 0;
2875 const GList *files = info->pairs;
2876 const GList *iter, *to_replace = NULL;
2878 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2879 SaveMimePartPair *pair = iter->data;
2880 if (modest_utils_file_exists (pair->filename)) {
2882 if (replaced_files == 1)
2886 if (replaced_files) {
2889 if (replaced_files == 1) {
2890 SaveMimePartPair *pair = to_replace->data;
2891 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2892 gchar *escaped_basename, *message;
2894 escaped_basename = g_uri_unescape_string (basename, NULL);
2895 message = g_strdup_printf ("%s\n%s",
2896 _FM("docm_nc_replace_file"),
2897 (escaped_basename) ? escaped_basename : "");
2898 response = modest_platform_run_confirmation_dialog (parent, message);
2900 g_free (escaped_basename);
2902 response = modest_platform_run_confirmation_dialog (parent,
2903 _FM("docm_nc_replace_multiple"));
2905 if (response != GTK_RESPONSE_OK)
2910 save_mime_part_info_free (info, TRUE);
2912 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2918 save_attachments_response (GtkDialog *dialog,
2922 TnyList *mime_parts;
2924 GList *files_to_save = NULL;
2925 gchar *current_folder;
2927 mime_parts = TNY_LIST (user_data);
2929 if (arg1 != GTK_RESPONSE_OK)
2932 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2933 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2934 if (current_folder && current_folder != '\0') {
2936 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2937 current_folder,&err);
2939 g_debug ("Error storing latest used folder: %s", err->message);
2943 g_free (current_folder);
2945 if (!modest_utils_folder_writable (chooser_uri)) {
2946 hildon_banner_show_information
2947 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2951 iter = tny_list_create_iterator (mime_parts);
2952 while (!tny_iterator_is_done (iter)) {
2953 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2955 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2956 !tny_mime_part_is_purged (mime_part) &&
2957 (tny_mime_part_get_filename (mime_part) != NULL)) {
2958 SaveMimePartPair *pair;
2960 pair = g_slice_new0 (SaveMimePartPair);
2962 if (tny_list_get_length (mime_parts) > 1) {
2964 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2965 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2968 pair->filename = g_strdup (chooser_uri);
2970 pair->part = mime_part;
2971 files_to_save = g_list_prepend (files_to_save, pair);
2973 tny_iterator_next (iter);
2975 g_object_unref (iter);
2978 if (files_to_save != NULL) {
2979 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2980 info->pairs = files_to_save;
2981 info->result = TRUE;
2982 info->uri = g_strdup (chooser_uri);
2983 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
2985 g_free (chooser_uri);
2988 /* Free and close the dialog */
2989 g_object_unref (mime_parts);
2990 gtk_widget_destroy (GTK_WIDGET (dialog));
2994 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2995 TnyList *mime_parts)
2997 ModestMsgViewWindowPrivate *priv;
2998 GtkWidget *save_dialog = NULL;
2999 gchar *conf_folder = NULL;
3000 gchar *filename = NULL;
3001 gchar *save_multiple_str = NULL;
3003 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3004 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3006 if (mime_parts == NULL) {
3007 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3008 * selection available */
3009 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3010 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3011 g_object_unref (mime_parts);
3014 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3016 g_object_unref (mime_parts);
3022 g_object_ref (mime_parts);
3025 /* prepare dialog */
3026 if (tny_list_get_length (mime_parts) == 1) {
3028 /* only one attachment selected */
3029 iter = tny_list_create_iterator (mime_parts);
3030 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3031 g_object_unref (iter);
3032 if (!modest_tny_mime_part_is_msg (mime_part) &&
3033 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3034 !tny_mime_part_is_purged (mime_part)) {
3035 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3037 /* TODO: show any error? */
3038 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3039 g_object_unref (mime_parts);
3042 g_object_unref (mime_part);
3044 gint num = tny_list_get_length (mime_parts);
3045 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3046 "sfil_va_number_of_objects_attachment",
3047 "sfil_va_number_of_objects_attachments",
3051 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3052 GTK_FILE_CHOOSER_ACTION_SAVE);
3055 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3056 if (conf_folder && conf_folder[0] != '\0') {
3057 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3060 /* Set the default folder to images folder */
3061 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3062 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3063 g_free (docs_folder);
3065 g_free (conf_folder);
3069 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3074 /* if multiple, set multiple string */
3075 if (save_multiple_str) {
3076 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3077 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3080 /* We must run this asynchronously, because the hildon dialog
3081 performs a gtk_dialog_run by itself which leads to gdk
3083 g_signal_connect (save_dialog, "response",
3084 G_CALLBACK (save_attachments_response), mime_parts);
3086 gtk_widget_show_all (save_dialog);
3090 show_remove_attachment_information (gpointer userdata)
3092 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3093 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3095 /* We're outside the main lock */
3096 gdk_threads_enter ();
3098 if (priv->remove_attachment_banner != NULL) {
3099 gtk_widget_destroy (priv->remove_attachment_banner);
3100 g_object_unref (priv->remove_attachment_banner);
3103 priv->remove_attachment_banner = g_object_ref (
3104 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3106 gdk_threads_leave ();
3112 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3114 ModestMsgViewWindowPrivate *priv;
3115 TnyList *mime_parts = NULL, *tmp;
3116 gchar *confirmation_message;
3122 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3123 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3125 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3126 * because we don't have selection
3128 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3130 /* Remove already purged messages from mime parts list. We use
3131 a copy of the list to remove items in the original one */
3132 tmp = tny_list_copy (mime_parts);
3133 iter = tny_list_create_iterator (tmp);
3134 while (!tny_iterator_is_done (iter)) {
3135 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3136 if (tny_mime_part_is_purged (part))
3137 tny_list_remove (mime_parts, (GObject *) part);
3139 g_object_unref (part);
3140 tny_iterator_next (iter);
3142 g_object_unref (tmp);
3143 g_object_unref (iter);
3145 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3146 tny_list_get_length (mime_parts) == 0) {
3147 g_object_unref (mime_parts);
3151 n_attachments = tny_list_get_length (mime_parts);
3152 if (n_attachments == 1) {
3156 iter = tny_list_create_iterator (mime_parts);
3157 part = (TnyMimePart *) tny_iterator_get_current (iter);
3158 g_object_unref (iter);
3159 if (modest_tny_mime_part_is_msg (part)) {
3161 header = tny_msg_get_header (TNY_MSG (part));
3162 filename = tny_header_dup_subject (header);
3163 g_object_unref (header);
3164 if (filename == NULL)
3165 filename = g_strdup (_("mail_va_no_subject"));
3167 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3169 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3171 g_object_unref (part);
3173 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3174 "mcen_nc_purge_files_text",
3175 n_attachments), n_attachments);
3177 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3178 confirmation_message);
3179 g_free (confirmation_message);
3181 if (response != GTK_RESPONSE_OK) {
3182 g_object_unref (mime_parts);
3186 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3188 iter = tny_list_create_iterator (mime_parts);
3189 while (!tny_iterator_is_done (iter)) {
3192 part = (TnyMimePart *) tny_iterator_get_current (iter);
3193 tny_mime_part_set_purged (TNY_MIME_PART (part));
3194 g_object_unref (part);
3195 tny_iterator_next (iter);
3197 g_object_unref (iter);
3199 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3200 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3201 tny_msg_rewrite_cache (msg);
3202 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3203 g_object_unref (msg);
3204 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3206 g_object_unref (mime_parts);
3208 if (priv->purge_timeout > 0) {
3209 g_source_remove (priv->purge_timeout);
3210 priv->purge_timeout = 0;
3213 if (priv->remove_attachment_banner) {
3214 gtk_widget_destroy (priv->remove_attachment_banner);
3215 g_object_unref (priv->remove_attachment_banner);
3216 priv->remove_attachment_banner = NULL;
3222 update_window_title (ModestMsgViewWindow *window)
3224 ModestMsgViewWindowPrivate *priv;
3226 TnyHeader *header = NULL;
3227 gchar *subject = NULL;
3229 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3231 /* Note that if the window is closed while we're retrieving
3232 the message, this widget could de deleted */
3233 if (!priv->msg_view)
3236 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3238 if (priv->other_body) {
3241 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3243 g_strstrip (description);
3244 subject = description;
3246 } else if (msg != NULL) {
3247 header = tny_msg_get_header (msg);
3248 subject = tny_header_dup_subject (header);
3249 g_object_unref (header);
3250 g_object_unref (msg);
3253 if ((subject == NULL)||(subject[0] == '\0')) {
3255 subject = g_strdup (_("mail_va_no_subject"));
3258 gtk_window_set_title (GTK_WINDOW (window), subject);
3263 on_move_focus (GtkWidget *widget,
3264 GtkDirectionType direction,
3267 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3271 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3273 GnomeVFSResult result;
3274 GnomeVFSHandle *handle = NULL;
3275 GnomeVFSFileInfo *info = NULL;
3278 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3279 if (result != GNOME_VFS_OK) {
3284 info = gnome_vfs_file_info_new ();
3285 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3286 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3287 /* We put a "safe" default size for going to cache */
3288 *expected_size = (300*1024);
3290 *expected_size = info->size;
3292 gnome_vfs_file_info_unref (info);
3294 stream = tny_vfs_stream_new (handle);
3303 TnyStream *output_stream;
3304 GtkWidget *msg_view;
3309 on_fetch_image_idle_refresh_view (gpointer userdata)
3312 FetchImageData *fidata = (FetchImageData *) userdata;
3314 gdk_threads_enter ();
3315 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3316 ModestMsgViewWindowPrivate *priv;
3318 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3319 priv->fetching_images--;
3320 gtk_widget_queue_draw (fidata->msg_view);
3321 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3323 gdk_threads_leave ();
3325 g_object_unref (fidata->msg_view);
3326 g_object_unref (fidata->window);
3327 g_slice_free (FetchImageData, fidata);
3332 on_fetch_image_thread (gpointer userdata)
3334 FetchImageData *fidata = (FetchImageData *) userdata;
3335 TnyStreamCache *cache;
3336 TnyStream *cache_stream;
3338 cache = modest_runtime_get_images_cache ();
3340 tny_stream_cache_get_stream (cache,
3342 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3343 (gpointer) fidata->uri);
3344 g_free (fidata->cache_id);
3345 g_free (fidata->uri);
3347 if (cache_stream != NULL) {
3350 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3353 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3354 if (G_UNLIKELY (nb_read < 0)) {
3356 } else if (G_LIKELY (nb_read > 0)) {
3357 gssize nb_written = 0;
3359 while (G_UNLIKELY (nb_written < nb_read)) {
3362 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3363 nb_read - nb_written);
3364 if (G_UNLIKELY (len < 0))
3370 tny_stream_close (cache_stream);
3371 g_object_unref (cache_stream);
3374 tny_stream_close (fidata->output_stream);
3375 g_object_unref (fidata->output_stream);
3377 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3383 on_fetch_image (ModestMsgView *msgview,
3386 ModestMsgViewWindow *window)
3388 const gchar *current_account;
3389 ModestMsgViewWindowPrivate *priv;
3390 FetchImageData *fidata;
3392 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3394 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3396 fidata = g_slice_new0 (FetchImageData);
3397 fidata->msg_view = g_object_ref (msgview);
3398 fidata->window = g_object_ref (window);
3399 fidata->uri = g_strdup (uri);
3400 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3401 fidata->output_stream = g_object_ref (stream);
3403 priv->fetching_images++;
3404 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3405 g_object_unref (fidata->output_stream);
3406 g_free (fidata->cache_id);
3407 g_free (fidata->uri);
3408 g_object_unref (fidata->msg_view);
3409 g_slice_free (FetchImageData, fidata);
3410 tny_stream_close (stream);
3411 priv->fetching_images--;
3412 update_progress_hint (window);
3415 update_progress_hint (window);
3421 setup_menu (ModestMsgViewWindow *self)
3423 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3425 /* Settings menu buttons */
3426 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3427 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3428 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3430 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3431 dngettext(GETTEXT_PACKAGE,
3432 "mcen_me_move_message",
3433 "mcen_me_move_messages",
3436 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3437 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3439 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3440 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3441 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3443 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3444 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3445 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3447 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3448 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3449 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3451 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3452 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3453 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3454 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3455 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3456 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3458 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3459 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3460 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3461 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3462 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3463 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3465 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3466 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3467 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3471 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3473 ModestMsgViewWindowPrivate *priv;
3474 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3475 GSList *recipients = NULL;
3477 gboolean contacts_to_add = FALSE;
3479 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3483 header = modest_msg_view_window_get_header (self);
3486 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3487 g_object_unref (header);
3489 recipients = modest_tny_msg_get_all_recipients_list (msg);
3490 g_object_unref (msg);
3493 if (recipients != NULL) {
3494 GtkWidget *picker_dialog;
3495 GtkWidget *selector;
3497 gchar *selected = NULL;
3499 selector = hildon_touch_selector_new_text ();
3500 g_object_ref (selector);
3502 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3503 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3504 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3505 (const gchar *) node->data);
3506 contacts_to_add = TRUE;
3510 if (contacts_to_add) {
3513 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3514 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3516 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3517 HILDON_TOUCH_SELECTOR (selector));
3519 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3521 if (picker_result == GTK_RESPONSE_OK) {
3522 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3524 gtk_widget_destroy (picker_dialog);
3527 modest_address_book_add_address (selected, (GtkWindow *) self);
3532 g_object_unref (selector);
3537 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3541 _modest_msg_view_window_map_event (GtkWidget *widget,
3545 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3547 update_progress_hint (self);
3553 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3555 ModestMsgViewWindowPrivate *priv;
3556 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3558 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3562 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3564 ModestMsgViewWindowPrivate *priv;
3565 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3567 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3569 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3573 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3575 ModestMsgViewWindowPrivate *priv;
3578 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3580 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3581 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3583 if (!message_reader (self, priv, header, NULL, NULL, priv->row_reference)) {
3584 g_warning ("Shouldn't happen, trying to reload a message failed");
3587 g_object_unref (header);
3591 update_branding (ModestMsgViewWindow *self)
3593 const gchar *account;
3594 const gchar *mailbox;
3595 ModestAccountMgr *mgr;
3596 ModestProtocol *protocol = NULL;
3597 gchar *service_name = NULL;
3598 const GdkPixbuf *service_icon = NULL;
3599 ModestMsgViewWindowPrivate *priv;
3601 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3603 account = modest_window_get_active_account (MODEST_WINDOW (self));
3604 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3606 mgr = modest_runtime_get_account_mgr ();
3608 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3609 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3610 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3612 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3613 account, mailbox, MODEST_ICON_SIZE_SMALL);
3617 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3618 g_free (service_name);