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);
2030 g_object_unref (folder);
2036 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
2038 ModestMsgViewWindowPrivate *priv;
2039 GtkTreePath *path= NULL;
2040 GtkTreeIter tmp_iter;
2042 gboolean retval = TRUE;
2043 GtkTreeRowReference *row_reference = NULL;
2045 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2046 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2048 if (!priv->row_reference)
2051 /* Update the next row reference if it's not valid. This could
2052 happen if for example the header which it was pointing to,
2053 was deleted. The best place to do it is in the row-deleted
2054 handler but the tinymail model do not work like the glib
2055 tree models and reports the deletion when the row is still
2057 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
2058 if (gtk_tree_row_reference_valid (priv->row_reference)) {
2059 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2060 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
2063 if (priv->next_row_reference)
2064 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
2068 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
2070 gtk_tree_model_get_iter (priv->header_model,
2073 gtk_tree_path_free (path);
2075 gtk_tree_model_get (priv->header_model, &tmp_iter,
2076 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2079 /* Read the message & show it */
2080 if (!message_reader (window, priv, header, NULL, NULL, row_reference)) {
2083 gtk_tree_row_reference_free (row_reference);
2086 g_object_unref (header);
2092 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
2094 ModestMsgViewWindowPrivate *priv = NULL;
2096 gboolean finished = FALSE;
2097 gboolean retval = FALSE;
2099 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
2100 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2102 /* Return inmediatly if there is no header model */
2103 if (!priv->header_model || !priv->row_reference)
2106 path = gtk_tree_row_reference_get_path (priv->row_reference);
2107 while (!finished && gtk_tree_path_prev (path)) {
2111 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2112 gtk_tree_model_get (priv->header_model, &iter,
2113 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2117 if (msg_is_visible (header, priv->is_outbox)) {
2118 GtkTreeRowReference *row_reference;
2119 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2120 /* Read the message & show it */
2121 retval = message_reader (window, priv, header, NULL, NULL, row_reference);
2122 gtk_tree_row_reference_free (row_reference);
2126 g_object_unref (header);
2130 gtk_tree_path_free (path);
2135 view_msg_cb (ModestMailOperation *mail_op,
2142 ModestMsgViewWindow *self = NULL;
2143 ModestMsgViewWindowPrivate *priv = NULL;
2144 GtkTreeRowReference *row_reference = NULL;
2146 /* Unregister the header (it was registered before creating the mail operation) */
2147 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2149 row_reference = (GtkTreeRowReference *) user_data;
2152 gtk_tree_row_reference_free (row_reference);
2153 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2155 /* Restore window title */
2156 update_window_title (self);
2157 g_object_unref (self);
2162 /* If there was any error */
2163 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2165 gtk_tree_row_reference_free (row_reference);
2166 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2168 /* Restore window title */
2169 update_window_title (self);
2170 g_object_unref (self);
2175 /* Get the window */
2176 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2177 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2178 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2180 /* Update the row reference */
2181 if (priv->row_reference != NULL) {
2182 gtk_tree_row_reference_free (priv->row_reference);
2183 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2184 if (priv->next_row_reference != NULL) {
2185 gtk_tree_row_reference_free (priv->next_row_reference);
2187 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2188 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2191 /* Mark header as read */
2192 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2193 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2195 /* Set new message */
2196 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2197 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2198 modest_msg_view_window_update_priority (self);
2199 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2200 update_branding (MODEST_MSG_VIEW_WINDOW (self));
2201 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2204 /* Set the new message uid of the window */
2205 if (priv->msg_uid) {
2206 g_free (priv->msg_uid);
2207 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2210 /* Notify the observers */
2211 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2212 0, priv->header_model, priv->row_reference);
2215 g_object_unref (self);
2217 gtk_tree_row_reference_free (row_reference);
2221 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2223 ModestMsgViewWindowPrivate *priv;
2225 TnyFolderType folder_type;
2227 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2229 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2231 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2235 folder = tny_msg_get_folder (msg);
2237 folder_type = modest_tny_folder_guess_folder_type (folder);
2238 g_object_unref (folder);
2240 g_object_unref (msg);
2248 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2250 ModestMsgViewWindowPrivate *priv;
2251 TnyHeader *header = NULL;
2252 TnyHeaderFlags flags = 0;
2254 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2256 if (priv->header_model && priv->row_reference) {
2258 GtkTreePath *path = NULL;
2260 path = gtk_tree_row_reference_get_path (priv->row_reference);
2261 g_return_if_fail (path != NULL);
2262 gtk_tree_model_get_iter (priv->header_model,
2264 gtk_tree_row_reference_get_path (priv->row_reference));
2266 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2268 gtk_tree_path_free (path);
2271 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2273 header = tny_msg_get_header (msg);
2274 g_object_unref (msg);
2279 flags = tny_header_get_flags (header);
2280 g_object_unref(G_OBJECT(header));
2283 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2288 toolbar_resize (ModestMsgViewWindow *self)
2290 ModestMsgViewWindowPrivate *priv = NULL;
2291 ModestWindowPrivate *parent_priv = NULL;
2293 gint static_button_size;
2294 ModestWindowMgr *mgr;
2296 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2297 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2298 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2300 mgr = modest_runtime_get_window_mgr ();
2301 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2303 if (parent_priv->toolbar) {
2304 /* left size buttons */
2305 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2306 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2307 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2308 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2309 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReplyAll");
2310 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2311 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2312 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2313 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2314 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2315 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2316 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2317 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDownloadExternalImages");
2318 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2319 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2320 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2322 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2323 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2324 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2325 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2330 modest_msg_view_window_show_toolbar (ModestWindow *self,
2331 gboolean show_toolbar)
2333 ModestMsgViewWindowPrivate *priv = NULL;
2334 ModestWindowPrivate *parent_priv;
2336 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2337 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2339 /* Set optimized view status */
2340 priv->optimized_view = !show_toolbar;
2342 if (!parent_priv->toolbar) {
2343 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2345 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2346 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2348 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2349 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2350 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2353 hildon_window_add_toolbar (HILDON_WINDOW (self),
2354 GTK_TOOLBAR (parent_priv->toolbar));
2359 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2360 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2361 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2363 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2364 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2365 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2367 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2370 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2371 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2376 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2378 ModestMsgViewWindow *window)
2380 if (!GTK_WIDGET_VISIBLE (window))
2383 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2387 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2389 ModestMsgViewWindowPrivate *priv;
2391 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2392 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2394 return priv->progress_hint;
2398 observers_empty (ModestMsgViewWindow *self)
2401 ModestMsgViewWindowPrivate *priv;
2402 gboolean is_empty = TRUE;
2403 guint pending_ops = 0;
2405 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2406 tmp = priv->progress_widgets;
2408 /* Check all observers */
2409 while (tmp && is_empty) {
2410 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2411 is_empty = pending_ops == 0;
2413 tmp = g_slist_next(tmp);
2420 on_account_removed (TnyAccountStore *account_store,
2421 TnyAccount *account,
2424 /* Do nothing if it's a transport account, because we only
2425 show the messages of a store account */
2426 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2427 const gchar *parent_acc = NULL;
2428 const gchar *our_acc = NULL;
2430 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2431 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2433 /* Close this window if I'm showing a message of the removed account */
2434 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2435 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2440 on_mail_operation_started (ModestMailOperation *mail_op,
2443 ModestMsgViewWindow *self;
2444 ModestMailOperationTypeOperation op_type;
2446 ModestMsgViewWindowPrivate *priv;
2447 GObject *source = NULL;
2449 self = MODEST_MSG_VIEW_WINDOW (user_data);
2450 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2451 op_type = modest_mail_operation_get_type_operation (mail_op);
2452 tmp = priv->progress_widgets;
2453 source = modest_mail_operation_get_source(mail_op);
2454 if (G_OBJECT (self) == source) {
2455 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2456 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2457 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2458 set_progress_hint (self, TRUE);
2460 modest_progress_object_add_operation (
2461 MODEST_PROGRESS_OBJECT (tmp->data),
2463 tmp = g_slist_next (tmp);
2467 g_object_unref (source);
2469 /* Update dimming rules */
2470 check_dimming_rules_after_change (self);
2474 on_mail_operation_finished (ModestMailOperation *mail_op,
2477 ModestMsgViewWindow *self;
2478 ModestMailOperationTypeOperation op_type;
2480 ModestMsgViewWindowPrivate *priv;
2482 self = MODEST_MSG_VIEW_WINDOW (user_data);
2483 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2484 op_type = modest_mail_operation_get_type_operation (mail_op);
2485 tmp = priv->progress_widgets;
2487 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ||
2488 op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ||
2489 op_type == MODEST_MAIL_OPERATION_TYPE_DELETE) {
2491 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2493 tmp = g_slist_next (tmp);
2496 /* If no more operations are being observed, NORMAL mode is enabled again */
2497 if (observers_empty (self)) {
2498 set_progress_hint (self, FALSE);
2502 /* Update dimming rules. We have to do this right here
2503 and not in view_msg_cb because at that point the
2504 transfer mode is still enabled so the dimming rule
2505 won't let the user delete the message that has been
2506 readed for example */
2507 check_dimming_rules_after_change (self);
2511 on_queue_changed (ModestMailOperationQueue *queue,
2512 ModestMailOperation *mail_op,
2513 ModestMailOperationQueueNotification type,
2514 ModestMsgViewWindow *self)
2516 ModestMsgViewWindowPrivate *priv;
2518 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2520 /* If this operations was created by another window, do nothing */
2521 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2524 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2525 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2527 "operation-started",
2528 G_CALLBACK (on_mail_operation_started),
2530 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2532 "operation-finished",
2533 G_CALLBACK (on_mail_operation_finished),
2535 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2536 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2538 "operation-started");
2539 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2541 "operation-finished");
2546 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2548 ModestMsgViewWindowPrivate *priv;
2549 TnyList *selected_attachments = NULL;
2551 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2552 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2554 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2555 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2557 return selected_attachments;
2561 ModestMsgViewWindow *self;
2563 } DecodeAsyncHelper;
2566 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2572 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2574 /* It could happen that the window was closed */
2575 if (GTK_WIDGET_VISIBLE (helper->self))
2576 set_progress_hint (helper->self, FALSE);
2578 if (cancelled || err) {
2580 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2581 modest_platform_information_banner (NULL, NULL, msg);
2587 /* make the file read-only */
2588 g_chmod(helper->file_path, 0444);
2590 /* Activate the file */
2591 modest_platform_activate_file (helper->file_path, tny_mime_part_get_content_type (mime_part));
2595 g_object_unref (helper->self);
2596 g_free (helper->file_path);
2597 g_slice_free (DecodeAsyncHelper, helper);
2601 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2602 TnyMimePart *mime_part)
2604 ModestMsgViewWindowPrivate *priv;
2605 const gchar *msg_uid;
2606 gchar *attachment_uid = NULL;
2607 gint attachment_index = 0;
2608 TnyList *attachments;
2610 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2611 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2612 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2614 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2615 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2616 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2617 g_object_unref (attachments);
2619 if (msg_uid && attachment_index >= 0) {
2620 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2623 if (mime_part == NULL) {
2624 gboolean error = FALSE;
2625 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2626 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2628 } else if (tny_list_get_length (selected_attachments) > 1) {
2629 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2633 iter = tny_list_create_iterator (selected_attachments);
2634 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2635 g_object_unref (iter);
2637 if (selected_attachments)
2638 g_object_unref (selected_attachments);
2643 g_object_ref (mime_part);
2646 if (tny_mime_part_is_purged (mime_part))
2649 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_get_filename (mime_part)) {
2650 gchar *filepath = NULL;
2651 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2652 gboolean show_error_banner = FALSE;
2653 TnyFsStream *temp_stream = NULL;
2654 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2657 if (temp_stream != NULL) {
2658 DecodeAsyncHelper *helper;
2660 /* Activate progress hint */
2661 set_progress_hint (window, TRUE);
2663 helper = g_slice_new0 (DecodeAsyncHelper);
2664 helper->self = g_object_ref (window);
2665 helper->file_path = g_strdup (filepath);
2667 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2668 on_decode_to_stream_async_handler,
2671 g_object_unref (temp_stream);
2672 /* NOTE: files in the temporary area will be automatically
2673 * cleaned after some time if they are no longer in use */
2676 const gchar *content_type;
2677 /* the file may already exist but it isn't writable,
2678 * let's try to open it anyway */
2679 content_type = tny_mime_part_get_content_type (mime_part);
2680 modest_platform_activate_file (filepath, content_type);
2682 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2683 show_error_banner = TRUE;
2688 if (show_error_banner)
2689 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2690 } else if (!modest_tny_mime_part_is_msg (mime_part)) {
2691 ModestWindowMgr *mgr;
2692 ModestWindow *msg_win = NULL;
2693 TnyMsg *current_msg;
2697 current_msg = modest_msg_view_window_get_message (MODEST_MSG_VIEW_WINDOW (window));
2698 mgr = modest_runtime_get_window_mgr ();
2699 header = tny_msg_get_header (TNY_MSG (current_msg));
2700 found = modest_window_mgr_find_registered_message_uid (mgr,
2705 g_debug ("window for this body is already being created");
2708 /* it's not found, so create a new window for it */
2709 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2710 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2711 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2713 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2715 msg_win = modest_msg_view_window_new_with_other_body (TNY_MSG (current_msg), TNY_MIME_PART (mime_part),
2716 account, mailbox, attachment_uid);
2718 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2719 modest_window_get_zoom (MODEST_WINDOW (window)));
2720 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2721 gtk_widget_show_all (GTK_WIDGET (msg_win));
2723 gtk_widget_destroy (GTK_WIDGET (msg_win));
2725 g_object_unref (current_msg);
2727 /* message attachment */
2728 TnyHeader *header = NULL;
2729 ModestWindowMgr *mgr;
2730 ModestWindow *msg_win = NULL;
2733 header = tny_msg_get_header (TNY_MSG (mime_part));
2734 mgr = modest_runtime_get_window_mgr ();
2735 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2738 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2739 * thus, we don't do anything */
2740 g_debug ("window for is already being created");
2742 /* it's not found, so create a new window for it */
2743 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2744 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2745 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2747 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2748 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2749 mailbox, attachment_uid);
2750 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2751 modest_window_get_zoom (MODEST_WINDOW (window)));
2752 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2753 gtk_widget_show_all (GTK_WIDGET (msg_win));
2755 gtk_widget_destroy (GTK_WIDGET (msg_win));
2761 g_free (attachment_uid);
2763 g_object_unref (mime_part);
2775 GnomeVFSResult result;
2779 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2780 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2781 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2782 static void save_mime_parts_to_file_with_checks (GtkWindow *parent, SaveMimePartInfo *info);
2785 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2789 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2790 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2791 g_free (pair->filename);
2792 g_object_unref (pair->part);
2793 g_slice_free (SaveMimePartPair, pair);
2795 g_list_free (info->pairs);
2799 g_slice_free (SaveMimePartInfo, info);
2804 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2806 /* This is a GDK lock because we are an idle callback and
2807 * hildon_banner_show_information is or does Gtk+ code */
2809 gdk_threads_enter (); /* CHECKED */
2810 if (info->result == GNOME_VFS_OK) {
2811 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2812 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2815 /* Check if the uri belongs to the external mmc */
2816 if (g_str_has_prefix (info->uri, g_getenv (MODEST_MMC1_VOLUMEPATH_ENV)))
2817 msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2819 msg = g_strdup (_KR("cerm_memory_card_full"));
2820 modest_platform_information_banner (NULL, NULL, msg);
2823 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2825 save_mime_part_info_free (info, FALSE);
2826 gdk_threads_leave (); /* CHECKED */
2832 save_mime_part_to_file (SaveMimePartInfo *info)
2834 GnomeVFSHandle *handle;
2836 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2838 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2839 if (info->result == GNOME_VFS_OK) {
2840 GError *error = NULL;
2841 stream = tny_vfs_stream_new (handle);
2842 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2843 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2845 if ((error->domain == TNY_ERROR_DOMAIN) &&
2846 (error->code == TNY_IO_ERROR_WRITE) &&
2847 (errno == ENOSPC)) {
2848 info->result = GNOME_VFS_ERROR_NO_SPACE;
2850 info->result = GNOME_VFS_ERROR_IO;
2853 g_object_unref (G_OBJECT (stream));
2855 g_warning ("Could not create save attachment %s: %s\n",
2856 pair->filename, gnome_vfs_result_to_string (info->result));
2859 /* Go on saving remaining files */
2860 info->pairs = g_list_remove_link (info->pairs, info->pairs);
2861 if (info->pairs != NULL) {
2862 save_mime_part_to_file (info);
2864 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2871 save_mime_parts_to_file_with_checks (GtkWindow *parent,
2872 SaveMimePartInfo *info)
2874 gboolean is_ok = TRUE;
2875 gint replaced_files = 0;
2876 const GList *files = info->pairs;
2877 const GList *iter, *to_replace = NULL;
2879 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2880 SaveMimePartPair *pair = iter->data;
2881 if (modest_utils_file_exists (pair->filename)) {
2883 if (replaced_files == 1)
2887 if (replaced_files) {
2890 if (replaced_files == 1) {
2891 SaveMimePartPair *pair = to_replace->data;
2892 const gchar *basename = strrchr (pair->filename, G_DIR_SEPARATOR) + 1;
2893 gchar *escaped_basename, *message;
2895 escaped_basename = g_uri_unescape_string (basename, NULL);
2896 message = g_strdup_printf ("%s\n%s",
2897 _FM("docm_nc_replace_file"),
2898 (escaped_basename) ? escaped_basename : "");
2899 response = modest_platform_run_confirmation_dialog (parent, message);
2901 g_free (escaped_basename);
2903 response = modest_platform_run_confirmation_dialog (parent,
2904 _FM("docm_nc_replace_multiple"));
2906 if (response != GTK_RESPONSE_OK)
2911 save_mime_part_info_free (info, TRUE);
2913 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2919 save_attachments_response (GtkDialog *dialog,
2923 TnyList *mime_parts;
2925 GList *files_to_save = NULL;
2926 gchar *current_folder;
2928 mime_parts = TNY_LIST (user_data);
2930 if (arg1 != GTK_RESPONSE_OK)
2933 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2934 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2935 if (current_folder && current_folder != '\0') {
2937 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2938 current_folder,&err);
2940 g_debug ("Error storing latest used folder: %s", err->message);
2944 g_free (current_folder);
2946 if (!modest_utils_folder_writable (chooser_uri)) {
2947 hildon_banner_show_information
2948 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2952 iter = tny_list_create_iterator (mime_parts);
2953 while (!tny_iterator_is_done (iter)) {
2954 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2956 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2957 !tny_mime_part_is_purged (mime_part) &&
2958 (tny_mime_part_get_filename (mime_part) != NULL)) {
2959 SaveMimePartPair *pair;
2961 pair = g_slice_new0 (SaveMimePartPair);
2963 if (tny_list_get_length (mime_parts) > 1) {
2965 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2966 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2969 pair->filename = g_strdup (chooser_uri);
2971 pair->part = mime_part;
2972 files_to_save = g_list_prepend (files_to_save, pair);
2974 tny_iterator_next (iter);
2976 g_object_unref (iter);
2979 if (files_to_save != NULL) {
2980 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2981 info->pairs = files_to_save;
2982 info->result = TRUE;
2983 info->uri = g_strdup (chooser_uri);
2984 save_mime_parts_to_file_with_checks ((GtkWindow *) dialog, info);
2986 g_free (chooser_uri);
2989 /* Free and close the dialog */
2990 g_object_unref (mime_parts);
2991 gtk_widget_destroy (GTK_WIDGET (dialog));
2995 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2996 TnyList *mime_parts)
2998 ModestMsgViewWindowPrivate *priv;
2999 GtkWidget *save_dialog = NULL;
3000 gchar *conf_folder = NULL;
3001 gchar *filename = NULL;
3002 gchar *save_multiple_str = NULL;
3004 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3005 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3007 if (mime_parts == NULL) {
3008 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
3009 * selection available */
3010 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3011 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
3012 g_object_unref (mime_parts);
3015 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
3017 g_object_unref (mime_parts);
3023 g_object_ref (mime_parts);
3026 /* prepare dialog */
3027 if (tny_list_get_length (mime_parts) == 1) {
3029 /* only one attachment selected */
3030 iter = tny_list_create_iterator (mime_parts);
3031 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
3032 g_object_unref (iter);
3033 if (!modest_tny_mime_part_is_msg (mime_part) &&
3034 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
3035 !tny_mime_part_is_purged (mime_part)) {
3036 filename = g_strdup (tny_mime_part_get_filename (mime_part));
3038 /* TODO: show any error? */
3039 g_warning ("%s: Tried to save a non-file attachment", __FUNCTION__);
3040 g_object_unref (mime_parts);
3043 g_object_unref (mime_part);
3045 gint num = tny_list_get_length (mime_parts);
3046 save_multiple_str = g_strdup_printf (dngettext("hildon-fm",
3047 "sfil_va_number_of_objects_attachment",
3048 "sfil_va_number_of_objects_attachments",
3052 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
3053 GTK_FILE_CHOOSER_ACTION_SAVE);
3056 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
3057 if (conf_folder && conf_folder[0] != '\0') {
3058 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
3061 /* Set the default folder to images folder */
3062 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
3063 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
3064 g_free (docs_folder);
3066 g_free (conf_folder);
3070 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
3075 /* if multiple, set multiple string */
3076 if (save_multiple_str) {
3077 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
3078 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
3081 /* We must run this asynchronously, because the hildon dialog
3082 performs a gtk_dialog_run by itself which leads to gdk
3084 g_signal_connect (save_dialog, "response",
3085 G_CALLBACK (save_attachments_response), mime_parts);
3087 gtk_widget_show_all (save_dialog);
3091 show_remove_attachment_information (gpointer userdata)
3093 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
3094 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3096 /* We're outside the main lock */
3097 gdk_threads_enter ();
3099 if (priv->remove_attachment_banner != NULL) {
3100 gtk_widget_destroy (priv->remove_attachment_banner);
3101 g_object_unref (priv->remove_attachment_banner);
3104 priv->remove_attachment_banner = g_object_ref (
3105 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
3107 gdk_threads_leave ();
3113 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
3115 ModestMsgViewWindowPrivate *priv;
3116 TnyList *mime_parts = NULL, *tmp;
3117 gchar *confirmation_message;
3123 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
3124 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3126 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
3127 * because we don't have selection
3129 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
3131 /* Remove already purged messages from mime parts list. We use
3132 a copy of the list to remove items in the original one */
3133 tmp = tny_list_copy (mime_parts);
3134 iter = tny_list_create_iterator (tmp);
3135 while (!tny_iterator_is_done (iter)) {
3136 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
3137 if (tny_mime_part_is_purged (part))
3138 tny_list_remove (mime_parts, (GObject *) part);
3140 g_object_unref (part);
3141 tny_iterator_next (iter);
3143 g_object_unref (tmp);
3144 g_object_unref (iter);
3146 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
3147 tny_list_get_length (mime_parts) == 0) {
3148 g_object_unref (mime_parts);
3152 n_attachments = tny_list_get_length (mime_parts);
3153 if (n_attachments == 1) {
3157 iter = tny_list_create_iterator (mime_parts);
3158 part = (TnyMimePart *) tny_iterator_get_current (iter);
3159 g_object_unref (iter);
3160 if (modest_tny_mime_part_is_msg (part)) {
3162 header = tny_msg_get_header (TNY_MSG (part));
3163 filename = tny_header_dup_subject (header);
3164 g_object_unref (header);
3165 if (filename == NULL)
3166 filename = g_strdup (_("mail_va_no_subject"));
3168 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3170 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3172 g_object_unref (part);
3174 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3175 "mcen_nc_purge_files_text",
3176 n_attachments), n_attachments);
3178 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3179 confirmation_message);
3180 g_free (confirmation_message);
3182 if (response != GTK_RESPONSE_OK) {
3183 g_object_unref (mime_parts);
3187 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3189 iter = tny_list_create_iterator (mime_parts);
3190 while (!tny_iterator_is_done (iter)) {
3193 part = (TnyMimePart *) tny_iterator_get_current (iter);
3194 tny_mime_part_set_purged (TNY_MIME_PART (part));
3195 g_object_unref (part);
3196 tny_iterator_next (iter);
3198 g_object_unref (iter);
3200 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3201 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3202 tny_msg_rewrite_cache (msg);
3203 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3204 g_object_unref (msg);
3205 update_branding (MODEST_MSG_VIEW_WINDOW (window));
3207 g_object_unref (mime_parts);
3209 if (priv->purge_timeout > 0) {
3210 g_source_remove (priv->purge_timeout);
3211 priv->purge_timeout = 0;
3214 if (priv->remove_attachment_banner) {
3215 gtk_widget_destroy (priv->remove_attachment_banner);
3216 g_object_unref (priv->remove_attachment_banner);
3217 priv->remove_attachment_banner = NULL;
3223 update_window_title (ModestMsgViewWindow *window)
3225 ModestMsgViewWindowPrivate *priv;
3227 TnyHeader *header = NULL;
3228 gchar *subject = NULL;
3230 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3232 /* Note that if the window is closed while we're retrieving
3233 the message, this widget could de deleted */
3234 if (!priv->msg_view)
3237 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3239 if (priv->other_body) {
3242 description = modest_tny_mime_part_get_header_value (priv->other_body, "Content-Description");
3244 g_strstrip (description);
3245 subject = description;
3247 } else if (msg != NULL) {
3248 header = tny_msg_get_header (msg);
3249 subject = tny_header_dup_subject (header);
3250 g_object_unref (header);
3251 g_object_unref (msg);
3254 if ((subject == NULL)||(subject[0] == '\0')) {
3256 subject = g_strdup (_("mail_va_no_subject"));
3259 gtk_window_set_title (GTK_WINDOW (window), subject);
3264 on_move_focus (GtkWidget *widget,
3265 GtkDirectionType direction,
3268 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3272 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3274 GnomeVFSResult result;
3275 GnomeVFSHandle *handle = NULL;
3276 GnomeVFSFileInfo *info = NULL;
3279 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3280 if (result != GNOME_VFS_OK) {
3285 info = gnome_vfs_file_info_new ();
3286 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3287 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3288 /* We put a "safe" default size for going to cache */
3289 *expected_size = (300*1024);
3291 *expected_size = info->size;
3293 gnome_vfs_file_info_unref (info);
3295 stream = tny_vfs_stream_new (handle);
3304 TnyStream *output_stream;
3305 GtkWidget *msg_view;
3310 on_fetch_image_idle_refresh_view (gpointer userdata)
3313 FetchImageData *fidata = (FetchImageData *) userdata;
3315 gdk_threads_enter ();
3316 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3317 ModestMsgViewWindowPrivate *priv;
3319 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (fidata->window);
3320 priv->fetching_images--;
3321 gtk_widget_queue_draw (fidata->msg_view);
3322 update_progress_hint (MODEST_MSG_VIEW_WINDOW (fidata->window));
3324 gdk_threads_leave ();
3326 g_object_unref (fidata->msg_view);
3327 g_object_unref (fidata->window);
3328 g_slice_free (FetchImageData, fidata);
3333 on_fetch_image_thread (gpointer userdata)
3335 FetchImageData *fidata = (FetchImageData *) userdata;
3336 TnyStreamCache *cache;
3337 TnyStream *cache_stream;
3339 cache = modest_runtime_get_images_cache ();
3341 tny_stream_cache_get_stream (cache,
3343 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3344 (gpointer) fidata->uri);
3345 g_free (fidata->cache_id);
3346 g_free (fidata->uri);
3348 if (cache_stream != NULL) {
3351 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3354 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3355 if (G_UNLIKELY (nb_read < 0)) {
3357 } else if (G_LIKELY (nb_read > 0)) {
3358 gssize nb_written = 0;
3360 while (G_UNLIKELY (nb_written < nb_read)) {
3363 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3364 nb_read - nb_written);
3365 if (G_UNLIKELY (len < 0))
3371 tny_stream_close (cache_stream);
3372 g_object_unref (cache_stream);
3375 tny_stream_close (fidata->output_stream);
3376 g_object_unref (fidata->output_stream);
3378 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3384 on_fetch_image (ModestMsgView *msgview,
3387 ModestMsgViewWindow *window)
3389 const gchar *current_account;
3390 ModestMsgViewWindowPrivate *priv;
3391 FetchImageData *fidata;
3393 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3395 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3397 fidata = g_slice_new0 (FetchImageData);
3398 fidata->msg_view = g_object_ref (msgview);
3399 fidata->window = g_object_ref (window);
3400 fidata->uri = g_strdup (uri);
3401 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3402 fidata->output_stream = g_object_ref (stream);
3404 priv->fetching_images++;
3405 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3406 g_object_unref (fidata->output_stream);
3407 g_free (fidata->cache_id);
3408 g_free (fidata->uri);
3409 g_object_unref (fidata->msg_view);
3410 g_slice_free (FetchImageData, fidata);
3411 tny_stream_close (stream);
3412 priv->fetching_images--;
3413 update_progress_hint (window);
3416 update_progress_hint (window);
3422 setup_menu (ModestMsgViewWindow *self)
3424 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3426 /* Settings menu buttons */
3427 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_find"), NULL,
3428 APP_MENU_CALLBACK (modest_msg_view_window_show_find_toolbar),
3429 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_find_in_msg));
3431 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self),
3432 dngettext(GETTEXT_PACKAGE,
3433 "mcen_me_move_message",
3434 "mcen_me_move_messages",
3437 APP_MENU_CALLBACK (modest_ui_actions_on_move_to),
3438 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_move_to));
3440 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3441 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3442 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3444 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3445 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3446 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3448 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3449 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3450 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3452 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3453 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3454 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3455 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3456 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3457 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3459 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3460 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3461 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3462 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3463 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3464 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3466 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_ti_message_properties"), NULL,
3467 APP_MENU_CALLBACK (modest_ui_actions_on_details),
3468 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_details));
3472 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3474 ModestMsgViewWindowPrivate *priv;
3475 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3476 GSList *recipients = NULL;
3478 gboolean contacts_to_add = FALSE;
3480 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3484 header = modest_msg_view_window_get_header (self);
3487 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3488 g_object_unref (header);
3490 recipients = modest_tny_msg_get_all_recipients_list (msg);
3491 g_object_unref (msg);
3494 if (recipients != NULL) {
3495 GtkWidget *picker_dialog;
3496 GtkWidget *selector;
3498 gchar *selected = NULL;
3500 selector = hildon_touch_selector_new_text ();
3501 g_object_ref (selector);
3503 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3504 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3505 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3506 (const gchar *) node->data);
3507 contacts_to_add = TRUE;
3511 if (contacts_to_add) {
3514 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3515 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3517 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3518 HILDON_TOUCH_SELECTOR (selector));
3520 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3522 if (picker_result == GTK_RESPONSE_OK) {
3523 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3525 gtk_widget_destroy (picker_dialog);
3528 modest_address_book_add_address (selected, (GtkWindow *) self);
3533 g_object_unref (selector);
3538 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3542 _modest_msg_view_window_map_event (GtkWidget *widget,
3546 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3548 update_progress_hint (self);
3554 modest_msg_view_window_fetch_images (ModestMsgViewWindow *self)
3556 ModestMsgViewWindowPrivate *priv;
3557 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3559 modest_msg_view_request_fetch_images (MODEST_MSG_VIEW (priv->msg_view));
3563 modest_msg_view_window_has_blocked_external_images (ModestMsgViewWindow *self)
3565 ModestMsgViewWindowPrivate *priv;
3566 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3568 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
3570 return modest_msg_view_has_blocked_external_images (MODEST_MSG_VIEW (priv->msg_view));
3574 modest_msg_view_window_reload (ModestMsgViewWindow *self)
3576 ModestMsgViewWindowPrivate *priv;
3579 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
3581 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3582 header = modest_msg_view_window_get_header (MODEST_MSG_VIEW_WINDOW (self));
3584 if (!message_reader (self, priv, header, NULL, NULL, priv->row_reference)) {
3585 g_warning ("Shouldn't happen, trying to reload a message failed");
3588 g_object_unref (header);
3592 update_branding (ModestMsgViewWindow *self)
3594 const gchar *account;
3595 const gchar *mailbox;
3596 ModestAccountMgr *mgr;
3597 ModestProtocol *protocol = NULL;
3598 gchar *service_name = NULL;
3599 const GdkPixbuf *service_icon = NULL;
3600 ModestMsgViewWindowPrivate *priv;
3602 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3604 account = modest_window_get_active_account (MODEST_WINDOW (self));
3605 mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (self));
3607 mgr = modest_runtime_get_account_mgr ();
3609 if (modest_account_mgr_account_is_multimailbox (mgr, account, &protocol)) {
3610 if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
3611 service_name = modest_account_protocol_get_service_name (MODEST_ACCOUNT_PROTOCOL (protocol),
3613 service_icon = modest_account_protocol_get_service_icon (MODEST_ACCOUNT_PROTOCOL (protocol),
3614 account, mailbox, MODEST_ICON_SIZE_SMALL);
3618 modest_msg_view_set_branding (MODEST_MSG_VIEW (priv->msg_view), service_name, service_icon);
3619 g_free (service_name);