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>
70 #define MYDOCS_ENV "MYDOCSDIR"
71 #define DOCS_FOLDER ".documents"
73 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
74 struct _ModestMsgViewWindowPrivate {
77 GtkWidget *main_scroll;
78 GtkWidget *find_toolbar;
81 /* Progress observers */
82 GSList *progress_widgets;
85 GtkWidget *prev_toolitem;
86 GtkWidget *next_toolitem;
87 gboolean progress_hint;
89 /* Optimized view enabled */
90 gboolean optimized_view;
92 /* Whether this was created via the *_new_for_search_result() function. */
93 gboolean is_search_result;
95 /* Whether the message is in outbox */
98 /* A reference to the @model of the header view
99 * to allow selecting previous/next messages,
100 * if the message is currently selected in the header view.
102 const gchar *header_folder_id;
103 GtkTreeModel *header_model;
104 GtkTreeRowReference *row_reference;
105 GtkTreeRowReference *next_row_reference;
107 gulong clipboard_change_handler;
108 gulong queue_change_handler;
109 gulong account_removed_handler;
110 gulong row_changed_handler;
111 gulong row_deleted_handler;
112 gulong row_inserted_handler;
113 gulong rows_reordered_handler;
116 GtkWidget *remove_attachment_banner;
123 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
124 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
125 static void modest_header_view_observer_init(
126 ModestHeaderViewObserverIface *iface_class);
127 static void modest_msg_view_window_finalize (GObject *obj);
128 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
130 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
131 ModestMsgViewWindow *obj);
132 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
133 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
137 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
138 static void modest_msg_view_window_set_zoom (ModestWindow *window,
140 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
141 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
142 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
145 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
147 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
148 gboolean show_toolbar);
150 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
152 ModestMsgViewWindow *window);
154 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
157 ModestMsgViewWindow *window);
159 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
161 ModestMsgViewWindow *window);
163 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
164 GtkTreePath *tree_path,
165 GtkTreeIter *tree_iter,
166 ModestMsgViewWindow *window);
168 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
172 ModestMsgViewWindow *window);
174 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
176 const gchar *tny_folder_id);
178 static void on_queue_changed (ModestMailOperationQueue *queue,
179 ModestMailOperation *mail_op,
180 ModestMailOperationQueueNotification type,
181 ModestMsgViewWindow *self);
183 static void on_account_removed (TnyAccountStore *account_store,
187 static void on_move_focus (GtkWidget *widget,
188 GtkDirectionType direction,
191 static void view_msg_cb (ModestMailOperation *mail_op,
198 static void set_progress_hint (ModestMsgViewWindow *self,
201 static void update_window_title (ModestMsgViewWindow *window);
203 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
204 static void init_window (ModestMsgViewWindow *obj);
206 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
208 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
210 static gboolean on_fetch_image (ModestMsgView *msgview,
213 ModestMsgViewWindow *window);
215 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
216 GtkScrollType scroll_type,
219 static gboolean message_reader (ModestMsgViewWindow *window,
220 ModestMsgViewWindowPrivate *priv,
222 GtkTreeRowReference *row_reference);
224 static void setup_menu (ModestMsgViewWindow *self);
225 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
230 /* list my signals */
237 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
238 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
242 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
243 MODEST_TYPE_MSG_VIEW_WINDOW, \
244 ModestMsgViewWindowPrivate))
246 static GtkWindowClass *parent_class = NULL;
248 /* uncomment the following if you have defined any signals */
249 static guint signals[LAST_SIGNAL] = {0};
252 modest_msg_view_window_get_type (void)
254 static GType my_type = 0;
256 static const GTypeInfo my_info = {
257 sizeof(ModestMsgViewWindowClass),
258 NULL, /* base init */
259 NULL, /* base finalize */
260 (GClassInitFunc) modest_msg_view_window_class_init,
261 NULL, /* class finalize */
262 NULL, /* class data */
263 sizeof(ModestMsgViewWindow),
265 (GInstanceInitFunc) modest_msg_view_window_init,
268 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
269 "ModestMsgViewWindow",
272 static const GInterfaceInfo modest_header_view_observer_info =
274 (GInterfaceInitFunc) modest_header_view_observer_init,
275 NULL, /* interface_finalize */
276 NULL /* interface_data */
279 g_type_add_interface_static (my_type,
280 MODEST_TYPE_HEADER_VIEW_OBSERVER,
281 &modest_header_view_observer_info);
287 save_state (ModestWindow *self)
289 modest_widget_memory_save (modest_runtime_get_conf (),
291 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
295 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
296 GtkScrollType scroll_type,
300 ModestMsgViewWindowPrivate *priv;
301 gboolean return_value;
303 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
304 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
309 add_scroll_binding (GtkBindingSet *binding_set,
311 GtkScrollType scroll)
313 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
315 gtk_binding_entry_add_signal (binding_set, keyval, 0,
317 GTK_TYPE_SCROLL_TYPE, scroll,
318 G_TYPE_BOOLEAN, FALSE);
319 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
321 GTK_TYPE_SCROLL_TYPE, scroll,
322 G_TYPE_BOOLEAN, FALSE);
326 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
328 GObjectClass *gobject_class;
329 HildonWindowClass *hildon_window_class;
330 ModestWindowClass *modest_window_class;
331 GtkBindingSet *binding_set;
333 gobject_class = (GObjectClass*) klass;
334 hildon_window_class = (HildonWindowClass *) klass;
335 modest_window_class = (ModestWindowClass *) klass;
337 parent_class = g_type_class_peek_parent (klass);
338 gobject_class->finalize = modest_msg_view_window_finalize;
340 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
341 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
342 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
343 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
344 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
345 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
347 modest_window_class->save_state_func = save_state;
349 klass->scroll_child = modest_msg_view_window_scroll_child;
351 signals[MSG_CHANGED_SIGNAL] =
352 g_signal_new ("msg-changed",
353 G_TYPE_FROM_CLASS (gobject_class),
355 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
357 modest_marshal_VOID__POINTER_POINTER,
358 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
360 signals[SCROLL_CHILD_SIGNAL] =
361 g_signal_new ("scroll-child",
362 G_TYPE_FROM_CLASS (gobject_class),
363 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
364 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
366 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
367 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
369 binding_set = gtk_binding_set_by_class (klass);
370 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
371 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
372 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
373 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
374 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
375 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
377 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
381 static void modest_header_view_observer_init(
382 ModestHeaderViewObserverIface *iface_class)
384 iface_class->update_func = modest_msg_view_window_update_model_replaced;
388 modest_msg_view_window_init (ModestMsgViewWindow *obj)
390 ModestMsgViewWindowPrivate *priv;
391 ModestWindowPrivate *parent_priv = NULL;
392 GtkActionGroup *action_group = NULL;
393 GError *error = NULL;
394 GdkPixbuf *window_icon;
396 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
397 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
398 parent_priv->ui_manager = gtk_ui_manager_new();
400 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
401 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
403 /* Add common actions */
404 gtk_action_group_add_actions (action_group,
405 modest_action_entries,
406 G_N_ELEMENTS (modest_action_entries),
408 gtk_action_group_add_toggle_actions (action_group,
409 msg_view_toggle_action_entries,
410 G_N_ELEMENTS (msg_view_toggle_action_entries),
413 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
414 g_object_unref (action_group);
416 /* Load the UI definition */
417 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
420 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
421 g_error_free (error);
426 /* Add accelerators */
427 gtk_window_add_accel_group (GTK_WINDOW (obj),
428 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
430 priv->is_search_result = FALSE;
431 priv->is_outbox = FALSE;
433 priv->msg_view = NULL;
434 priv->header_model = NULL;
435 priv->header_folder_id = NULL;
436 priv->clipboard_change_handler = 0;
437 priv->queue_change_handler = 0;
438 priv->account_removed_handler = 0;
439 priv->row_changed_handler = 0;
440 priv->row_deleted_handler = 0;
441 priv->row_inserted_handler = 0;
442 priv->rows_reordered_handler = 0;
443 priv->progress_hint = FALSE;
445 priv->optimized_view = FALSE;
446 priv->purge_timeout = 0;
447 priv->remove_attachment_banner = NULL;
448 priv->msg_uid = NULL;
450 priv->sighandlers = NULL;
453 init_window (MODEST_MSG_VIEW_WINDOW(obj));
455 /* Set window icon */
456 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
458 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
459 g_object_unref (window_icon);
462 hildon_program_add_window (hildon_program_get_instance(),
469 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
471 ModestMsgViewWindowPrivate *priv = NULL;
473 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
475 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
477 set_progress_hint (self, TRUE);
483 set_progress_hint (ModestMsgViewWindow *self,
486 ModestWindowPrivate *parent_priv;
487 ModestMsgViewWindowPrivate *priv;
489 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
491 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
492 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
494 /* Sets current progress hint */
495 priv->progress_hint = enabled;
497 if (GTK_WIDGET_VISIBLE (self)) {
498 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
505 init_window (ModestMsgViewWindow *obj)
507 GtkWidget *main_vbox;
508 ModestMsgViewWindowPrivate *priv;
510 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
512 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
513 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
514 main_vbox = gtk_vbox_new (FALSE, 6);
515 #ifdef MODEST_TOOLKIT_HILDON2
516 priv->main_scroll = hildon_pannable_area_new ();
517 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
519 #ifdef MODEST_USE_MOZEMBED
520 priv->main_scroll = priv->msg_view;
521 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
523 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
524 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
526 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
527 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
528 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
531 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
532 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
534 priv->find_toolbar = hildon_find_toolbar_new (NULL);
535 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
536 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
538 /* NULL-ize fields if the window is destroyed */
539 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
541 gtk_widget_show_all (GTK_WIDGET(main_vbox));
545 modest_msg_view_window_disconnect_signals (ModestWindow *self)
547 ModestMsgViewWindowPrivate *priv;
548 GtkWidget *header_view = NULL;
549 GtkWindow *parent_window = NULL;
551 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
553 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
554 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
555 priv->clipboard_change_handler))
556 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
557 priv->clipboard_change_handler);
559 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
560 priv->queue_change_handler))
561 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
562 priv->queue_change_handler);
564 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
565 priv->account_removed_handler))
566 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
567 priv->account_removed_handler);
569 if (priv->header_model) {
570 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
571 priv->row_changed_handler))
572 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
573 priv->row_changed_handler);
575 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
576 priv->row_deleted_handler))
577 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
578 priv->row_deleted_handler);
580 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
581 priv->row_inserted_handler))
582 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
583 priv->row_inserted_handler);
585 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
586 priv->rows_reordered_handler))
587 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
588 priv->rows_reordered_handler);
591 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
592 priv->sighandlers = NULL;
594 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
595 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
596 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
598 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
599 MODEST_HEADER_VIEW_OBSERVER(self));
605 modest_msg_view_window_finalize (GObject *obj)
607 ModestMsgViewWindowPrivate *priv;
609 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
611 /* Sanity check: shouldn't be needed, the window mgr should
612 call this function before */
613 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
615 if (priv->header_model != NULL) {
616 g_object_unref (priv->header_model);
617 priv->header_model = NULL;
620 if (priv->remove_attachment_banner) {
621 gtk_widget_destroy (priv->remove_attachment_banner);
622 g_object_unref (priv->remove_attachment_banner);
623 priv->remove_attachment_banner = NULL;
626 if (priv->purge_timeout > 0) {
627 g_source_remove (priv->purge_timeout);
628 priv->purge_timeout = 0;
631 if (priv->row_reference) {
632 gtk_tree_row_reference_free (priv->row_reference);
633 priv->row_reference = NULL;
636 if (priv->next_row_reference) {
637 gtk_tree_row_reference_free (priv->next_row_reference);
638 priv->next_row_reference = NULL;
642 g_free (priv->msg_uid);
643 priv->msg_uid = NULL;
646 G_OBJECT_CLASS(parent_class)->finalize (obj);
650 select_next_valid_row (GtkTreeModel *model,
651 GtkTreeRowReference **row_reference,
655 GtkTreeIter tmp_iter;
657 GtkTreePath *next = NULL;
658 gboolean retval = FALSE, finished;
660 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
662 path = gtk_tree_row_reference_get_path (*row_reference);
663 gtk_tree_model_get_iter (model, &tmp_iter, path);
664 gtk_tree_row_reference_free (*row_reference);
665 *row_reference = NULL;
669 TnyHeader *header = NULL;
671 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
672 gtk_tree_model_get (model, &tmp_iter,
673 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
677 if (msg_is_visible (header, is_outbox)) {
678 next = gtk_tree_model_get_path (model, &tmp_iter);
679 *row_reference = gtk_tree_row_reference_new (model, next);
680 gtk_tree_path_free (next);
684 g_object_unref (header);
687 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
688 next = gtk_tree_model_get_path (model, &tmp_iter);
690 /* Ensure that we are not selecting the same */
691 if (gtk_tree_path_compare (path, next) != 0) {
692 gtk_tree_model_get (model, &tmp_iter,
693 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
696 if (msg_is_visible (header, is_outbox)) {
697 *row_reference = gtk_tree_row_reference_new (model, next);
701 g_object_unref (header);
705 /* If we ended up in the same message
706 then there is no valid next
710 gtk_tree_path_free (next);
712 /* If there are no more messages and we don't
713 want to start again in the first one then
714 there is no valid next message */
720 gtk_tree_path_free (path);
725 /* TODO: This should be in _init(), with the parameters as properties. */
727 modest_msg_view_window_construct (ModestMsgViewWindow *self,
728 const gchar *modest_account_name,
729 const gchar *mailbox,
730 const gchar *msg_uid)
733 ModestMsgViewWindowPrivate *priv = NULL;
734 ModestWindowPrivate *parent_priv = NULL;
735 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
736 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
738 obj = G_OBJECT (self);
739 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
740 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
742 priv->msg_uid = g_strdup (msg_uid);
745 parent_priv->menubar = NULL;
747 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
748 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
751 /* Add common dimming rules */
752 modest_dimming_rules_group_add_rules (toolbar_rules_group,
753 modest_msg_view_toolbar_dimming_entries,
754 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
755 MODEST_WINDOW (self));
756 modest_dimming_rules_group_add_rules (clipboard_rules_group,
757 modest_msg_view_clipboard_dimming_entries,
758 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
759 MODEST_WINDOW (self));
761 /* Insert dimming rules group for this window */
762 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
763 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
764 g_object_unref (toolbar_rules_group);
765 g_object_unref (clipboard_rules_group);
767 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
769 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);
770 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
771 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
772 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
773 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
774 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
775 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
776 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
777 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
778 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
779 G_CALLBACK (modest_ui_actions_on_details), obj);
780 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
781 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
782 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
783 G_CALLBACK (on_fetch_image), obj);
785 g_signal_connect (G_OBJECT (obj), "key-release-event",
786 G_CALLBACK (modest_msg_view_window_key_event),
789 g_signal_connect (G_OBJECT (obj), "key-press-event",
790 G_CALLBACK (modest_msg_view_window_key_event),
793 g_signal_connect (G_OBJECT (obj), "move-focus",
794 G_CALLBACK (on_move_focus), obj);
796 g_signal_connect (G_OBJECT (obj), "map-event",
797 G_CALLBACK (_modest_msg_view_window_map_event),
800 /* Mail Operation Queue */
801 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
803 G_CALLBACK (on_queue_changed),
806 /* Account manager */
807 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
809 G_CALLBACK(on_account_removed),
812 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
813 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
815 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
816 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
817 priv->last_search = NULL;
819 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
821 /* Init the clipboard actions dim status */
822 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
824 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
829 /* FIXME: parameter checks */
831 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
832 const gchar *modest_account_name,
833 const gchar *mailbox,
834 const gchar *msg_uid,
836 GtkTreeRowReference *row_reference)
838 ModestMsgViewWindow *window = NULL;
839 ModestMsgViewWindowPrivate *priv = NULL;
840 TnyFolder *header_folder = NULL;
841 ModestHeaderView *header_view = NULL;
842 ModestWindow *main_window = NULL;
843 ModestWindowMgr *mgr = NULL;
846 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
849 mgr = modest_runtime_get_window_mgr ();
850 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
851 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
853 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
855 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
857 /* Remember the message list's TreeModel so we can detect changes
858 * and change the list selection when necessary: */
860 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
862 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
863 MODEST_MAIN_WINDOW(main_window),
864 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
867 if (header_view != NULL){
868 header_folder = modest_header_view_get_folder(header_view);
869 /* This could happen if the header folder was
870 unseleted before opening this msg window (for
871 example if the user selects an account in the
872 folder view of the main window */
874 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
875 priv->header_folder_id = tny_folder_get_id(header_folder);
876 g_assert(priv->header_folder_id != NULL);
877 g_object_unref(header_folder);
881 /* Setup row references and connect signals */
882 priv->header_model = g_object_ref (model);
885 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
886 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
887 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
889 priv->row_reference = NULL;
890 priv->next_row_reference = NULL;
893 /* Connect signals */
894 priv->row_changed_handler =
895 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
896 G_CALLBACK(modest_msg_view_window_on_row_changed),
898 priv->row_deleted_handler =
899 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
900 G_CALLBACK(modest_msg_view_window_on_row_deleted),
902 priv->row_inserted_handler =
903 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
904 G_CALLBACK(modest_msg_view_window_on_row_inserted),
906 priv->rows_reordered_handler =
907 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
908 G_CALLBACK(modest_msg_view_window_on_row_reordered),
911 if (header_view != NULL){
912 modest_header_view_add_observer(header_view,
913 MODEST_HEADER_VIEW_OBSERVER(window));
916 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
917 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
919 /* gtk_widget_show_all (GTK_WIDGET (window)); */
920 modest_msg_view_window_update_priority (window);
921 /* Check dimming rules */
922 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
923 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
924 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
926 return MODEST_WINDOW(window);
930 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
931 const gchar *modest_account_name,
932 const gchar *mailbox,
933 const gchar *msg_uid,
934 GtkTreeRowReference *row_reference)
936 ModestMsgViewWindow *window = NULL;
937 ModestMsgViewWindowPrivate *priv = NULL;
938 TnyFolder *header_folder = NULL;
939 ModestWindowMgr *mgr = NULL;
943 mgr = modest_runtime_get_window_mgr ();
944 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
945 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
947 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
949 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
951 /* Remember the message list's TreeModel so we can detect changes
952 * and change the list selection when necessary: */
954 if (header_view != NULL){
955 header_folder = modest_header_view_get_folder(header_view);
956 /* This could happen if the header folder was
957 unseleted before opening this msg window (for
958 example if the user selects an account in the
959 folder view of the main window */
961 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
962 priv->header_folder_id = tny_folder_get_id(header_folder);
963 g_assert(priv->header_folder_id != NULL);
964 g_object_unref(header_folder);
968 /* Setup row references and connect signals */
969 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
970 g_object_ref (priv->header_model);
973 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
974 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
975 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
977 priv->row_reference = NULL;
978 priv->next_row_reference = NULL;
981 /* Connect signals */
982 priv->row_changed_handler =
983 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
984 G_CALLBACK(modest_msg_view_window_on_row_changed),
986 priv->row_deleted_handler =
987 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
988 G_CALLBACK(modest_msg_view_window_on_row_deleted),
990 priv->row_inserted_handler =
991 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
992 G_CALLBACK(modest_msg_view_window_on_row_inserted),
994 priv->rows_reordered_handler =
995 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
996 G_CALLBACK(modest_msg_view_window_on_row_reordered),
999 if (header_view != NULL){
1000 modest_header_view_add_observer(header_view,
1001 MODEST_HEADER_VIEW_OBSERVER(window));
1004 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1006 path = gtk_tree_row_reference_get_path (row_reference);
1007 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1009 gtk_tree_model_get (priv->header_model, &iter,
1010 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1012 message_reader (window, priv, header, row_reference);
1014 gtk_tree_path_free (path);
1016 /* Check dimming rules */
1017 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1018 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1019 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1021 return MODEST_WINDOW(window);
1025 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1026 const gchar *modest_account_name,
1027 const gchar *mailbox,
1028 const gchar *msg_uid)
1030 ModestMsgViewWindow *window = NULL;
1031 ModestMsgViewWindowPrivate *priv = NULL;
1032 ModestWindowMgr *mgr = NULL;
1034 mgr = modest_runtime_get_window_mgr ();
1035 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1036 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1037 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1039 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1041 /* Remember that this is a search result,
1042 * so we can disable some UI appropriately: */
1043 priv->is_search_result = TRUE;
1045 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1047 update_window_title (window);
1048 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1049 modest_msg_view_window_update_priority (window);
1051 /* Check dimming rules */
1052 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1053 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1054 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1056 return MODEST_WINDOW(window);
1060 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1061 const gchar *modest_account_name,
1062 const gchar *mailbox,
1063 const gchar *msg_uid)
1065 GObject *obj = NULL;
1066 ModestMsgViewWindowPrivate *priv;
1067 ModestWindowMgr *mgr = NULL;
1069 g_return_val_if_fail (msg, NULL);
1070 mgr = modest_runtime_get_window_mgr ();
1071 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1072 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1073 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1074 modest_account_name, mailbox, msg_uid);
1076 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1077 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1079 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1081 /* Check dimming rules */
1082 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1083 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1084 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1086 return MODEST_WINDOW(obj);
1090 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1093 ModestMsgViewWindow *window)
1095 check_dimming_rules_after_change (window);
1099 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1101 ModestMsgViewWindow *window)
1103 check_dimming_rules_after_change (window);
1105 /* The window could have dissapeared */
1108 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1110 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1111 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1115 /* On insertions we check if the folder still has the message we are
1116 * showing or do not. If do not, we do nothing. Which means we are still
1117 * not attached to any header folder and thus next/prev buttons are
1118 * still dimmed. Once the message that is shown by msg-view is found, the
1119 * new model of header-view will be attached and the references will be set.
1120 * On each further insertions dimming rules will be checked. However
1121 * this requires extra CPU time at least works.
1122 * (An message might be deleted from TnyFolder and thus will not be
1123 * inserted into the model again for example if it is removed by the
1124 * imap server and the header view is refreshed.)
1127 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1128 GtkTreePath *tree_path,
1129 GtkTreeIter *tree_iter,
1130 ModestMsgViewWindow *window)
1132 ModestMsgViewWindowPrivate *priv = NULL;
1133 TnyHeader *header = NULL;
1135 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1136 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1138 g_assert (model == priv->header_model);
1140 /* Check if the newly inserted message is the same we are actually
1141 * showing. IF not, we should remain detached from the header model
1142 * and thus prev and next toolbar buttons should remain dimmed. */
1143 gtk_tree_model_get (model, tree_iter,
1144 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1147 if (TNY_IS_HEADER (header)) {
1150 uid = modest_tny_folder_get_header_unique_id (header);
1151 if (!g_str_equal(priv->msg_uid, uid)) {
1152 check_dimming_rules_after_change (window);
1154 g_object_unref (G_OBJECT(header));
1158 g_object_unref(G_OBJECT(header));
1161 if (priv->row_reference) {
1162 gtk_tree_row_reference_free (priv->row_reference);
1165 /* Setup row_reference for the actual msg. */
1166 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1167 if (priv->row_reference == NULL) {
1168 g_warning("No reference for msg header item.");
1172 /* Now set up next_row_reference. */
1173 if (priv->next_row_reference) {
1174 gtk_tree_row_reference_free (priv->next_row_reference);
1177 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1178 select_next_valid_row (priv->header_model,
1179 &(priv->next_row_reference), FALSE, priv->is_outbox);
1181 /* Connect the remaining callbacks to become able to detect
1182 * changes in header-view. */
1183 priv->row_changed_handler =
1184 g_signal_connect (priv->header_model, "row-changed",
1185 G_CALLBACK (modest_msg_view_window_on_row_changed),
1187 priv->row_deleted_handler =
1188 g_signal_connect (priv->header_model, "row-deleted",
1189 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1191 priv->rows_reordered_handler =
1192 g_signal_connect (priv->header_model, "rows-reordered",
1193 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1196 check_dimming_rules_after_change (window);
1200 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1204 ModestMsgViewWindow *window)
1206 ModestMsgViewWindowPrivate *priv = NULL;
1207 gboolean already_changed = FALSE;
1209 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1211 /* If the current row was reordered select the proper next
1212 valid row. The same if the next row reference changes */
1213 if (priv->row_reference &&
1214 gtk_tree_row_reference_valid (priv->row_reference)) {
1216 path = gtk_tree_row_reference_get_path (priv->row_reference);
1217 if (gtk_tree_path_compare (path, arg1) == 0) {
1218 if (priv->next_row_reference) {
1219 gtk_tree_row_reference_free (priv->next_row_reference);
1221 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1222 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1223 already_changed = TRUE;
1225 gtk_tree_path_free (path);
1227 if (!already_changed &&
1228 priv->next_row_reference &&
1229 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1231 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1232 if (gtk_tree_path_compare (path, arg1) == 0) {
1233 if (priv->next_row_reference) {
1234 gtk_tree_row_reference_free (priv->next_row_reference);
1236 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1237 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1239 gtk_tree_path_free (path);
1241 check_dimming_rules_after_change (window);
1244 /* The modest_msg_view_window_update_model_replaced implements update
1245 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1246 * actually belongs to the header-view is the same as the TnyFolder of
1247 * the message of msg-view or not. If they are different, there is
1248 * nothing to do. If they are the same, then the model has replaced and
1249 * the reference in msg-view shall be replaced from the old model to
1250 * the new model. In this case the view will be detached from it's
1251 * header folder. From this point the next/prev buttons are dimmed.
1254 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1255 GtkTreeModel *model,
1256 const gchar *tny_folder_id)
1258 ModestMsgViewWindowPrivate *priv = NULL;
1259 ModestMsgViewWindow *window = NULL;
1261 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1262 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1264 window = MODEST_MSG_VIEW_WINDOW(observer);
1265 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1267 /* If there is an other folder in the header-view then we do
1268 * not care about it's model (msg list). Else if the
1269 * header-view shows the folder the msg shown by us is in, we
1270 * shall replace our model reference and make some check. */
1271 if(model == NULL || tny_folder_id == NULL ||
1272 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1275 /* Model is changed(replaced), so we should forget the old
1276 * one. Because there might be other references and there
1277 * might be some change on the model even if we unreferenced
1278 * it, we need to disconnect our signals here. */
1279 if (priv->header_model) {
1280 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1281 priv->row_changed_handler))
1282 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1283 priv->row_changed_handler);
1284 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1285 priv->row_deleted_handler))
1286 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1287 priv->row_deleted_handler);
1288 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1289 priv->row_inserted_handler))
1290 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1291 priv->row_inserted_handler);
1292 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1293 priv->rows_reordered_handler))
1294 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1295 priv->rows_reordered_handler);
1298 if (priv->row_reference)
1299 gtk_tree_row_reference_free (priv->row_reference);
1300 if (priv->next_row_reference)
1301 gtk_tree_row_reference_free (priv->next_row_reference);
1302 g_object_unref(priv->header_model);
1305 priv->row_changed_handler = 0;
1306 priv->row_deleted_handler = 0;
1307 priv->row_inserted_handler = 0;
1308 priv->rows_reordered_handler = 0;
1309 priv->next_row_reference = NULL;
1310 priv->row_reference = NULL;
1311 priv->header_model = NULL;
1314 priv->header_model = g_object_ref (model);
1316 /* Also we must connect to the new model for row insertions.
1317 * Only for insertions now. We will need other ones only after
1318 * the msg is show by msg-view is added to the new model. */
1319 priv->row_inserted_handler =
1320 g_signal_connect (priv->header_model, "row-inserted",
1321 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1324 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1325 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1329 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1331 ModestMsgViewWindowPrivate *priv= NULL;
1333 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1334 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1336 return priv->progress_hint;
1340 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1342 ModestMsgViewWindowPrivate *priv= NULL;
1344 TnyHeader *header = NULL;
1345 GtkTreePath *path = NULL;
1348 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1349 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1351 /* If the message was not obtained from a treemodel,
1352 * for instance if it was opened directly by the search UI:
1354 if (priv->header_model == NULL ||
1355 priv->row_reference == NULL ||
1356 !gtk_tree_row_reference_valid (priv->row_reference)) {
1357 msg = modest_msg_view_window_get_message (self);
1359 header = tny_msg_get_header (msg);
1360 g_object_unref (msg);
1365 /* Get iter of the currently selected message in the header view: */
1366 path = gtk_tree_row_reference_get_path (priv->row_reference);
1367 g_return_val_if_fail (path != NULL, NULL);
1368 gtk_tree_model_get_iter (priv->header_model,
1372 /* Get current message header */
1373 gtk_tree_model_get (priv->header_model, &iter,
1374 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1377 gtk_tree_path_free (path);
1382 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1384 ModestMsgViewWindowPrivate *priv;
1386 g_return_val_if_fail (self, NULL);
1388 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1390 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1394 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1396 ModestMsgViewWindowPrivate *priv;
1398 g_return_val_if_fail (self, NULL);
1400 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1402 return (const gchar*) priv->msg_uid;
1406 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1409 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1410 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1411 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1415 is_active = gtk_toggle_action_get_active (toggle);
1418 gtk_widget_show (priv->find_toolbar);
1419 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1421 gtk_widget_hide (priv->find_toolbar);
1422 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1425 /* update the toggle buttons status */
1426 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1428 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1433 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1434 ModestMsgViewWindow *obj)
1436 GtkToggleAction *toggle;
1437 ModestWindowPrivate *parent_priv;
1438 ModestMsgViewWindowPrivate *priv;
1440 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1441 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1443 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1444 gtk_toggle_action_set_active (toggle, FALSE);
1445 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1449 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1450 ModestMsgViewWindow *obj)
1452 gchar *current_search;
1453 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1455 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1456 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1460 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1462 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1463 g_free (current_search);
1464 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1468 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1470 g_free (priv->last_search);
1471 priv->last_search = g_strdup (current_search);
1472 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1475 hildon_banner_show_information (NULL, NULL,
1476 _HL("ckct_ib_find_no_matches"));
1477 g_free (priv->last_search);
1478 priv->last_search = NULL;
1480 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1483 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1484 hildon_banner_show_information (NULL, NULL,
1485 _HL("ckct_ib_find_search_complete"));
1486 g_free (priv->last_search);
1487 priv->last_search = NULL;
1489 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1493 g_free (current_search);
1498 modest_msg_view_window_set_zoom (ModestWindow *window,
1501 ModestMsgViewWindowPrivate *priv;
1502 ModestWindowPrivate *parent_priv;
1504 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1506 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1507 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1508 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1513 modest_msg_view_window_get_zoom (ModestWindow *window)
1515 ModestMsgViewWindowPrivate *priv;
1517 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1519 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1520 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1524 modest_msg_view_window_zoom_plus (ModestWindow *window)
1527 ModestMsgViewWindowPrivate *priv;
1531 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1532 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1534 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1536 if (zoom_level >= 2.0) {
1537 hildon_banner_show_information (NULL, NULL,
1538 _CS("ckct_ib_max_zoom_level_reached"));
1540 } else if (zoom_level >= 1.5) {
1542 } else if (zoom_level >= 1.2) {
1544 } else if (zoom_level >= 1.0) {
1546 } else if (zoom_level >= 0.8) {
1548 } else if (zoom_level >= 0.5) {
1554 /* set zoom level */
1555 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1556 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1557 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1558 g_free (banner_text);
1559 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1565 modest_msg_view_window_zoom_minus (ModestWindow *window)
1568 ModestMsgViewWindowPrivate *priv;
1572 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1573 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1575 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1577 if (zoom_level <= 0.5) {
1578 hildon_banner_show_information (NULL, NULL,
1579 _CS("ckct_ib_min_zoom_level_reached"));
1581 } else if (zoom_level <= 0.8) {
1583 } else if (zoom_level <= 1.0) {
1585 } else if (zoom_level <= 1.2) {
1587 } else if (zoom_level <= 1.5) {
1589 } else if (zoom_level <= 2.0) {
1595 /* set zoom level */
1596 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1597 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1598 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1599 g_free (banner_text);
1600 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1607 modest_msg_view_window_key_event (GtkWidget *window,
1613 focus = gtk_window_get_focus (GTK_WINDOW (window));
1615 /* for the find toolbar case */
1616 if (focus && GTK_IS_ENTRY (focus)) {
1617 if (event->keyval == GDK_BackSpace) {
1619 copy = gdk_event_copy ((GdkEvent *) event);
1620 gtk_widget_event (focus, copy);
1621 gdk_event_free (copy);
1626 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1627 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1628 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1629 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1630 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1631 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1632 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1633 /* gboolean return_value; */
1635 if (event->type == GDK_KEY_PRESS) {
1636 GtkScrollType scroll_type;
1638 switch (event->keyval) {
1641 scroll_type = GTK_SCROLL_STEP_UP; break;
1644 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1646 case GDK_KP_Page_Up:
1647 scroll_type = GTK_SCROLL_PAGE_UP; break;
1649 case GDK_KP_Page_Down:
1650 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1653 scroll_type = GTK_SCROLL_START; break;
1656 scroll_type = GTK_SCROLL_END; break;
1657 default: scroll_type = GTK_SCROLL_NONE;
1660 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1661 /* scroll_type, FALSE, &return_value); */
1672 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1675 ModestMsgViewWindowPrivate *priv;
1676 GtkTreeIter tmp_iter;
1677 gboolean is_last_selected;
1679 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1680 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1682 /*if no model (so no rows at all), then virtually we are the last*/
1683 if (!priv->header_model || !priv->row_reference)
1686 if (!gtk_tree_row_reference_valid (priv->row_reference))
1689 path = gtk_tree_row_reference_get_path (priv->row_reference);
1693 is_last_selected = TRUE;
1694 while (is_last_selected) {
1696 gtk_tree_path_next (path);
1697 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1699 gtk_tree_model_get (priv->header_model, &tmp_iter,
1700 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1703 if (msg_is_visible (header, priv->is_outbox))
1704 is_last_selected = FALSE;
1705 g_object_unref(G_OBJECT(header));
1708 gtk_tree_path_free (path);
1709 return is_last_selected;
1713 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1715 ModestMsgViewWindowPrivate *priv;
1717 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1718 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1720 return priv->header_model != NULL;
1724 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1726 ModestMsgViewWindowPrivate *priv;
1728 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1729 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1731 return priv->is_search_result;
1735 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1737 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1739 if (!check_outbox) {
1742 ModestTnySendQueueStatus status;
1743 status = modest_tny_all_send_queues_get_msg_status (header);
1744 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1745 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1750 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1753 ModestMsgViewWindowPrivate *priv;
1754 gboolean is_first_selected;
1755 GtkTreeIter tmp_iter;
1757 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1758 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1760 /*if no model (so no rows at all), then virtually we are the first*/
1761 if (!priv->header_model || !priv->row_reference)
1764 if (!gtk_tree_row_reference_valid (priv->row_reference))
1767 path = gtk_tree_row_reference_get_path (priv->row_reference);
1771 is_first_selected = TRUE;
1772 while (is_first_selected) {
1774 if(!gtk_tree_path_prev (path))
1776 /* Here the 'if' is needless for logic, but let make sure
1777 * iter is valid for gtk_tree_model_get. */
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_first_selected = FALSE;
1786 g_object_unref(G_OBJECT(header));
1789 gtk_tree_path_free (path);
1790 return is_first_selected;
1795 GtkTreeRowReference *row_reference;
1799 message_reader_performer (gboolean canceled,
1801 GtkWindow *parent_window,
1802 TnyAccount *account,
1805 ModestMailOperation *mail_op = NULL;
1806 MsgReaderInfo *info;
1808 info = (MsgReaderInfo *) user_data;
1809 if (canceled || err) {
1810 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1814 /* Register the header - it'll be unregistered in the callback */
1815 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1817 /* New mail operation */
1818 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1819 modest_ui_actions_disk_operations_error_handler,
1822 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1823 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1824 g_object_unref (mail_op);
1826 /* Update dimming rules */
1827 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1828 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1831 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1832 g_object_unref (info->header);
1833 g_slice_free (MsgReaderInfo, info);
1838 * Reads the message whose summary item is @header. It takes care of
1839 * several things, among others:
1841 * If the message was not previously downloaded then ask the user
1842 * before downloading. If there is no connection launch the connection
1843 * dialog. Update toolbar dimming rules.
1845 * Returns: TRUE if the mail operation was started, otherwise if the
1846 * user do not want to download the message, or if the user do not
1847 * want to connect, then the operation is not issued
1850 message_reader (ModestMsgViewWindow *window,
1851 ModestMsgViewWindowPrivate *priv,
1853 GtkTreeRowReference *row_reference)
1855 ModestWindowMgr *mgr;
1856 TnyAccount *account;
1858 MsgReaderInfo *info;
1860 g_return_val_if_fail (row_reference != NULL, FALSE);
1862 mgr = modest_runtime_get_window_mgr ();
1863 /* Msg download completed */
1864 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1866 /* We set the header from model while we're loading */
1867 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1868 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1870 /* Ask the user if he wants to download the message if
1872 if (!tny_device_is_online (modest_runtime_get_device())) {
1873 GtkResponseType response;
1875 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1876 _("mcen_nc_get_msg"));
1877 if (response == GTK_RESPONSE_CANCEL) {
1878 update_window_title (window);
1882 folder = tny_header_get_folder (header);
1883 info = g_slice_new (MsgReaderInfo);
1884 info->header = g_object_ref (header);
1885 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1887 /* Offer the connection dialog if necessary */
1888 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1890 TNY_FOLDER_STORE (folder),
1891 message_reader_performer,
1893 g_object_unref (folder);
1898 folder = tny_header_get_folder (header);
1899 account = tny_folder_get_account (folder);
1900 info = g_slice_new (MsgReaderInfo);
1901 info->header = g_object_ref (header);
1902 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1904 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1905 g_object_unref (account);
1906 g_object_unref (folder);
1912 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1914 ModestMsgViewWindowPrivate *priv;
1915 GtkTreePath *path= NULL;
1916 GtkTreeIter tmp_iter;
1918 gboolean retval = TRUE;
1919 GtkTreeRowReference *row_reference = NULL;
1921 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1922 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1924 if (!priv->row_reference)
1927 /* Update the next row reference if it's not valid. This could
1928 happen if for example the header which it was pointing to,
1929 was deleted. The best place to do it is in the row-deleted
1930 handler but the tinymail model do not work like the glib
1931 tree models and reports the deletion when the row is still
1933 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1934 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1935 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1936 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1939 if (priv->next_row_reference)
1940 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1944 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1946 gtk_tree_model_get_iter (priv->header_model,
1949 gtk_tree_path_free (path);
1951 gtk_tree_model_get (priv->header_model, &tmp_iter,
1952 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1955 /* Read the message & show it */
1956 if (!message_reader (window, priv, header, row_reference)) {
1959 gtk_tree_row_reference_free (row_reference);
1962 g_object_unref (header);
1968 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1970 ModestMsgViewWindowPrivate *priv = NULL;
1972 gboolean finished = FALSE;
1973 gboolean retval = FALSE;
1975 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1976 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1978 /* Return inmediatly if there is no header model */
1979 if (!priv->header_model || !priv->row_reference)
1982 path = gtk_tree_row_reference_get_path (priv->row_reference);
1983 while (!finished && gtk_tree_path_prev (path)) {
1987 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1988 gtk_tree_model_get (priv->header_model, &iter,
1989 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1993 if (msg_is_visible (header, priv->is_outbox)) {
1994 GtkTreeRowReference *row_reference;
1995 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1996 /* Read the message & show it */
1997 retval = message_reader (window, priv, header, row_reference);
1998 gtk_tree_row_reference_free (row_reference);
2002 g_object_unref (header);
2006 gtk_tree_path_free (path);
2011 view_msg_cb (ModestMailOperation *mail_op,
2018 ModestMsgViewWindow *self = NULL;
2019 ModestMsgViewWindowPrivate *priv = NULL;
2020 GtkTreeRowReference *row_reference = NULL;
2022 /* Unregister the header (it was registered before creating the mail operation) */
2023 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2025 row_reference = (GtkTreeRowReference *) user_data;
2027 gtk_tree_row_reference_free (row_reference);
2028 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2030 /* Restore window title */
2031 update_window_title (self);
2032 g_object_unref (self);
2037 /* If there was any error */
2038 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2039 gtk_tree_row_reference_free (row_reference);
2040 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2042 /* Restore window title */
2043 update_window_title (self);
2044 g_object_unref (self);
2049 /* Get the window */
2050 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2051 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2052 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2054 /* Update the row reference */
2055 if (priv->row_reference != NULL) {
2056 gtk_tree_row_reference_free (priv->row_reference);
2057 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2058 if (priv->next_row_reference != NULL) {
2059 gtk_tree_row_reference_free (priv->next_row_reference);
2061 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2062 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2065 /* Mark header as read */
2066 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2067 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2069 /* Set new message */
2070 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2071 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2072 modest_msg_view_window_update_priority (self);
2073 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2074 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2077 /* Set the new message uid of the window */
2078 if (priv->msg_uid) {
2079 g_free (priv->msg_uid);
2080 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2083 /* Notify the observers */
2084 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2085 0, priv->header_model, priv->row_reference);
2088 g_object_unref (self);
2089 gtk_tree_row_reference_free (row_reference);
2093 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2095 ModestMsgViewWindowPrivate *priv;
2097 TnyFolderType folder_type;
2099 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2101 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2103 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2107 folder = tny_msg_get_folder (msg);
2109 folder_type = modest_tny_folder_guess_folder_type (folder);
2110 g_object_unref (folder);
2112 g_object_unref (msg);
2120 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2122 ModestMsgViewWindowPrivate *priv;
2123 TnyHeader *header = NULL;
2124 TnyHeaderFlags flags = 0;
2126 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2128 if (priv->header_model && priv->row_reference) {
2130 GtkTreePath *path = NULL;
2132 path = gtk_tree_row_reference_get_path (priv->row_reference);
2133 g_return_if_fail (path != NULL);
2134 gtk_tree_model_get_iter (priv->header_model,
2136 gtk_tree_row_reference_get_path (priv->row_reference));
2138 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2140 gtk_tree_path_free (path);
2143 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2145 header = tny_msg_get_header (msg);
2146 g_object_unref (msg);
2151 flags = tny_header_get_flags (header);
2152 g_object_unref(G_OBJECT(header));
2155 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2160 toolbar_resize (ModestMsgViewWindow *self)
2162 ModestMsgViewWindowPrivate *priv = NULL;
2163 ModestWindowPrivate *parent_priv = NULL;
2165 gint static_button_size;
2166 ModestWindowMgr *mgr;
2168 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2169 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2170 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2172 mgr = modest_runtime_get_window_mgr ();
2173 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2175 if (parent_priv->toolbar) {
2176 /* left size buttons */
2177 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2178 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2179 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2180 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2181 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2182 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2183 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2184 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2185 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2186 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2187 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2188 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2189 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2190 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2191 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2192 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2194 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2195 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2196 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2197 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2202 modest_msg_view_window_show_toolbar (ModestWindow *self,
2203 gboolean show_toolbar)
2205 ModestMsgViewWindowPrivate *priv = NULL;
2206 ModestWindowPrivate *parent_priv;
2208 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2209 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2211 /* Set optimized view status */
2212 priv->optimized_view = !show_toolbar;
2214 if (!parent_priv->toolbar) {
2215 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2217 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2218 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2220 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2221 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2222 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2225 hildon_window_add_toolbar (HILDON_WINDOW (self),
2226 GTK_TOOLBAR (parent_priv->toolbar));
2231 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2232 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2233 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2235 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2236 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2237 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2239 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2242 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2243 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2248 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2250 ModestMsgViewWindow *window)
2252 if (!GTK_WIDGET_VISIBLE (window))
2255 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2259 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2261 ModestMsgViewWindowPrivate *priv;
2263 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2264 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2266 return priv->progress_hint;
2270 observers_empty (ModestMsgViewWindow *self)
2273 ModestMsgViewWindowPrivate *priv;
2274 gboolean is_empty = TRUE;
2275 guint pending_ops = 0;
2277 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2278 tmp = priv->progress_widgets;
2280 /* Check all observers */
2281 while (tmp && is_empty) {
2282 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2283 is_empty = pending_ops == 0;
2285 tmp = g_slist_next(tmp);
2292 on_account_removed (TnyAccountStore *account_store,
2293 TnyAccount *account,
2296 /* Do nothing if it's a transport account, because we only
2297 show the messages of a store account */
2298 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2299 const gchar *parent_acc = NULL;
2300 const gchar *our_acc = NULL;
2302 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2303 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2305 /* Close this window if I'm showing a message of the removed account */
2306 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2307 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2312 on_mail_operation_started (ModestMailOperation *mail_op,
2315 ModestMsgViewWindow *self;
2316 ModestMailOperationTypeOperation op_type;
2318 ModestMsgViewWindowPrivate *priv;
2319 GObject *source = NULL;
2321 self = MODEST_MSG_VIEW_WINDOW (user_data);
2322 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2323 op_type = modest_mail_operation_get_type_operation (mail_op);
2324 tmp = priv->progress_widgets;
2325 source = modest_mail_operation_get_source(mail_op);
2326 if (G_OBJECT (self) == source) {
2327 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2328 set_toolbar_transfer_mode(self);
2330 modest_progress_object_add_operation (
2331 MODEST_PROGRESS_OBJECT (tmp->data),
2333 tmp = g_slist_next (tmp);
2337 g_object_unref (source);
2341 on_mail_operation_finished (ModestMailOperation *mail_op,
2344 ModestMsgViewWindow *self;
2345 ModestMailOperationTypeOperation op_type;
2347 ModestMsgViewWindowPrivate *priv;
2349 self = MODEST_MSG_VIEW_WINDOW (user_data);
2350 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2351 op_type = modest_mail_operation_get_type_operation (mail_op);
2352 tmp = priv->progress_widgets;
2354 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2356 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2358 tmp = g_slist_next (tmp);
2361 /* If no more operations are being observed, NORMAL mode is enabled again */
2362 if (observers_empty (self)) {
2363 set_progress_hint (self, FALSE);
2367 /* Update dimming rules. We have to do this right here
2368 and not in view_msg_cb because at that point the
2369 transfer mode is still enabled so the dimming rule
2370 won't let the user delete the message that has been
2371 readed for example */
2372 check_dimming_rules_after_change (self);
2377 on_queue_changed (ModestMailOperationQueue *queue,
2378 ModestMailOperation *mail_op,
2379 ModestMailOperationQueueNotification type,
2380 ModestMsgViewWindow *self)
2382 ModestMsgViewWindowPrivate *priv;
2384 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2386 /* If this operations was created by another window, do nothing */
2387 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2390 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2391 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2393 "operation-started",
2394 G_CALLBACK (on_mail_operation_started),
2396 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2398 "operation-finished",
2399 G_CALLBACK (on_mail_operation_finished),
2401 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2402 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2404 "operation-started");
2405 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2407 "operation-finished");
2412 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2414 ModestMsgViewWindowPrivate *priv;
2415 TnyList *selected_attachments = NULL;
2417 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2418 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2420 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2421 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2423 return selected_attachments;
2427 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2433 gchar *filepath = (gchar *) user_data;
2435 if (cancelled || err) {
2437 modest_platform_information_banner (NULL, NULL,
2438 _KR("cerm_device_memory_full"));
2443 /* make the file read-only */
2444 g_chmod(filepath, 0444);
2446 /* Activate the file */
2447 modest_platform_activate_file (filepath, tny_mime_part_get_content_type (mime_part));
2455 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2456 TnyMimePart *mime_part)
2458 ModestMsgViewWindowPrivate *priv;
2459 const gchar *msg_uid;
2460 gchar *attachment_uid = NULL;
2461 gint attachment_index = 0;
2462 TnyList *attachments;
2464 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2465 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2466 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2468 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2469 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2470 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2471 g_object_unref (attachments);
2473 if (msg_uid && attachment_index >= 0) {
2474 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2477 if (mime_part == NULL) {
2478 gboolean error = FALSE;
2479 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2480 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2482 } else if (tny_list_get_length (selected_attachments) > 1) {
2483 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2487 iter = tny_list_create_iterator (selected_attachments);
2488 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2489 g_object_unref (iter);
2491 if (selected_attachments)
2492 g_object_unref (selected_attachments);
2497 g_object_ref (mime_part);
2500 if (tny_mime_part_is_purged (mime_part))
2503 if (!modest_tny_mime_part_is_msg (mime_part)) {
2504 gchar *filepath = NULL;
2505 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2506 gboolean show_error_banner = FALSE;
2507 TnyFsStream *temp_stream = NULL;
2508 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2511 if (temp_stream != NULL) {
2512 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2513 on_decode_to_stream_async_handler,
2515 g_strdup (filepath));
2516 g_object_unref (temp_stream);
2517 /* NOTE: files in the temporary area will be automatically
2518 * cleaned after some time if they are no longer in use */
2521 const gchar *content_type;
2522 /* the file may already exist but it isn't writable,
2523 * let's try to open it anyway */
2524 content_type = tny_mime_part_get_content_type (mime_part);
2525 modest_platform_activate_file (filepath, content_type);
2527 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2528 show_error_banner = TRUE;
2533 if (show_error_banner)
2534 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2536 /* message attachment */
2537 TnyHeader *header = NULL;
2538 ModestWindowMgr *mgr;
2539 ModestWindow *msg_win = NULL;
2542 header = tny_msg_get_header (TNY_MSG (mime_part));
2543 mgr = modest_runtime_get_window_mgr ();
2544 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2547 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2548 * thus, we don't do anything */
2549 g_warning ("window for is already being created");
2551 /* it's not found, so create a new window for it */
2552 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2553 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2554 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2556 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2557 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2558 mailbox, attachment_uid);
2559 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2560 modest_window_get_zoom (MODEST_WINDOW (window)));
2561 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2562 gtk_widget_show_all (GTK_WIDGET (msg_win));
2564 gtk_widget_destroy (GTK_WIDGET (msg_win));
2570 g_free (attachment_uid);
2572 g_object_unref (mime_part);
2584 GnomeVFSResult result;
2587 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2588 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2589 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2590 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2593 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2597 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2598 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2599 g_free (pair->filename);
2600 g_object_unref (pair->part);
2601 g_slice_free (SaveMimePartPair, pair);
2603 g_list_free (info->pairs);
2606 g_slice_free (SaveMimePartInfo, info);
2611 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2613 if (info->pairs != NULL) {
2614 save_mime_part_to_file (info);
2616 /* This is a GDK lock because we are an idle callback and
2617 * hildon_banner_show_information is or does Gtk+ code */
2619 gdk_threads_enter (); /* CHECKED */
2620 save_mime_part_info_free (info, TRUE);
2621 if (info->result == GNOME_VFS_OK) {
2622 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2623 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2624 hildon_banner_show_information (NULL, NULL,
2625 _KR("cerm_device_memory_full"));
2627 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2629 gdk_threads_leave (); /* CHECKED */
2636 save_mime_part_to_file (SaveMimePartInfo *info)
2638 GnomeVFSHandle *handle;
2640 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2642 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2643 if (info->result == GNOME_VFS_OK) {
2644 GError *error = NULL;
2645 stream = tny_vfs_stream_new (handle);
2646 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2647 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2649 if ((error->domain == TNY_ERROR_DOMAIN) &&
2650 (error->code == TNY_IO_ERROR_WRITE) &&
2651 (errno == ENOSPC)) {
2652 info->result = GNOME_VFS_ERROR_NO_SPACE;
2654 info->result = GNOME_VFS_ERROR_IO;
2657 g_object_unref (G_OBJECT (stream));
2658 g_object_unref (pair->part);
2659 g_slice_free (SaveMimePartPair, pair);
2660 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2662 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2663 save_mime_part_info_free (info, FALSE);
2666 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2671 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2673 gboolean is_ok = TRUE;
2674 gint replaced_files = 0;
2675 const GList *files = info->pairs;
2678 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2679 SaveMimePartPair *pair = iter->data;
2680 if (modest_utils_file_exists (pair->filename)) {
2684 if (replaced_files) {
2685 GtkWidget *confirm_overwrite_dialog;
2686 const gchar *message = (replaced_files == 1) ?
2687 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2688 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2689 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2692 gtk_widget_destroy (confirm_overwrite_dialog);
2696 save_mime_part_info_free (info, TRUE);
2698 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2704 save_attachments_response (GtkDialog *dialog,
2708 TnyList *mime_parts;
2710 GList *files_to_save = NULL;
2711 gchar *current_folder;
2713 mime_parts = TNY_LIST (user_data);
2715 if (arg1 != GTK_RESPONSE_OK)
2718 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2719 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2720 if (current_folder && current_folder != '\0') {
2722 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2723 current_folder,&err);
2725 g_debug ("Error storing latest used folder: %s", err->message);
2729 g_free (current_folder);
2731 if (!modest_utils_folder_writable (chooser_uri)) {
2732 hildon_banner_show_information
2733 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2737 iter = tny_list_create_iterator (mime_parts);
2738 while (!tny_iterator_is_done (iter)) {
2739 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2741 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2742 !tny_mime_part_is_purged (mime_part) &&
2743 (tny_mime_part_get_filename (mime_part) != NULL)) {
2744 SaveMimePartPair *pair;
2746 pair = g_slice_new0 (SaveMimePartPair);
2748 if (tny_list_get_length (mime_parts) > 1) {
2750 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2751 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2754 pair->filename = g_strdup (chooser_uri);
2756 pair->part = mime_part;
2757 files_to_save = g_list_prepend (files_to_save, pair);
2759 tny_iterator_next (iter);
2761 g_object_unref (iter);
2763 g_free (chooser_uri);
2765 if (files_to_save != NULL) {
2766 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2767 info->pairs = files_to_save;
2768 info->result = TRUE;
2769 save_mime_parts_to_file_with_checks (info);
2773 /* Free and close the dialog */
2774 g_object_unref (mime_parts);
2775 gtk_widget_destroy (GTK_WIDGET (dialog));
2779 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2780 TnyList *mime_parts)
2782 ModestMsgViewWindowPrivate *priv;
2783 GtkWidget *save_dialog = NULL;
2784 gchar *conf_folder = NULL;
2785 gchar *filename = NULL;
2786 gchar *save_multiple_str = NULL;
2788 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2789 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2791 if (mime_parts == NULL) {
2792 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2793 * selection available */
2794 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2795 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2796 g_object_unref (mime_parts);
2799 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2801 g_object_unref (mime_parts);
2807 g_object_ref (mime_parts);
2810 /* prepare dialog */
2811 if (tny_list_get_length (mime_parts) == 1) {
2813 /* only one attachment selected */
2814 iter = tny_list_create_iterator (mime_parts);
2815 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2816 g_object_unref (iter);
2817 if (!modest_tny_mime_part_is_msg (mime_part) &&
2818 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2819 !tny_mime_part_is_purged (mime_part)) {
2820 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2822 /* TODO: show any error? */
2823 g_warning ("Tried to save a non-file attachment");
2824 g_object_unref (mime_parts);
2827 g_object_unref (mime_part);
2829 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2830 tny_list_get_length (mime_parts));
2833 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2834 GTK_FILE_CHOOSER_ACTION_SAVE);
2837 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2838 if (conf_folder && conf_folder[0] != '\0') {
2839 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2842 /* Set the default folder to images folder */
2843 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2844 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2845 g_free (docs_folder);
2847 g_free (conf_folder);
2851 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2856 /* if multiple, set multiple string */
2857 if (save_multiple_str) {
2858 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2859 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2862 /* We must run this asynchronously, because the hildon dialog
2863 performs a gtk_dialog_run by itself which leads to gdk
2865 g_signal_connect (save_dialog, "response",
2866 G_CALLBACK (save_attachments_response), mime_parts);
2868 gtk_widget_show_all (save_dialog);
2872 show_remove_attachment_information (gpointer userdata)
2874 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2875 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2877 /* We're outside the main lock */
2878 gdk_threads_enter ();
2880 if (priv->remove_attachment_banner != NULL) {
2881 gtk_widget_destroy (priv->remove_attachment_banner);
2882 g_object_unref (priv->remove_attachment_banner);
2885 priv->remove_attachment_banner = g_object_ref (
2886 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2888 gdk_threads_leave ();
2894 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2896 ModestMsgViewWindowPrivate *priv;
2897 TnyList *mime_parts = NULL;
2898 gchar *confirmation_message;
2904 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2905 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2907 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2908 * because we don't have selection
2910 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2912 /* Remove already purged messages from mime parts list */
2913 iter = tny_list_create_iterator (mime_parts);
2914 while (!tny_iterator_is_done (iter)) {
2915 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2916 tny_iterator_next (iter);
2917 if (tny_mime_part_is_purged (part)) {
2918 tny_list_remove (mime_parts, (GObject *) part);
2920 g_object_unref (part);
2922 g_object_unref (iter);
2924 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
2925 tny_list_get_length (mime_parts) == 0) {
2926 g_object_unref (mime_parts);
2930 n_attachments = tny_list_get_length (mime_parts);
2931 if (n_attachments == 1) {
2935 iter = tny_list_create_iterator (mime_parts);
2936 part = (TnyMimePart *) tny_iterator_get_current (iter);
2937 g_object_unref (iter);
2938 if (modest_tny_mime_part_is_msg (part)) {
2940 header = tny_msg_get_header (TNY_MSG (part));
2941 filename = tny_header_dup_subject (header);
2942 g_object_unref (header);
2943 if (filename == NULL)
2944 filename = g_strdup (_("mail_va_no_subject"));
2946 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2948 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2950 g_object_unref (part);
2952 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2953 "mcen_nc_purge_files_text",
2954 n_attachments), n_attachments);
2956 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2957 confirmation_message);
2958 g_free (confirmation_message);
2960 if (response != GTK_RESPONSE_OK) {
2961 g_object_unref (mime_parts);
2965 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2967 iter = tny_list_create_iterator (mime_parts);
2968 while (!tny_iterator_is_done (iter)) {
2971 part = (TnyMimePart *) tny_iterator_get_current (iter);
2972 tny_mime_part_set_purged (TNY_MIME_PART (part));
2973 g_object_unref (part);
2974 tny_iterator_next (iter);
2976 g_object_unref (iter);
2978 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2979 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2980 tny_msg_rewrite_cache (msg);
2981 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2982 g_object_unref (msg);
2984 g_object_unref (mime_parts);
2986 if (priv->purge_timeout > 0) {
2987 g_source_remove (priv->purge_timeout);
2988 priv->purge_timeout = 0;
2991 if (priv->remove_attachment_banner) {
2992 gtk_widget_destroy (priv->remove_attachment_banner);
2993 g_object_unref (priv->remove_attachment_banner);
2994 priv->remove_attachment_banner = NULL;
3002 update_window_title (ModestMsgViewWindow *window)
3004 ModestMsgViewWindowPrivate *priv;
3006 TnyHeader *header = NULL;
3007 gchar *subject = NULL;
3009 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3011 /* Note that if the window is closed while we're retrieving
3012 the message, this widget could de deleted */
3013 if (!priv->msg_view)
3016 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3019 header = tny_msg_get_header (msg);
3020 subject = tny_header_dup_subject (header);
3021 g_object_unref (header);
3022 g_object_unref (msg);
3025 if ((subject == NULL)||(subject[0] == '\0')) {
3027 subject = g_strdup (_("mail_va_no_subject"));
3030 gtk_window_set_title (GTK_WINDOW (window), subject);
3035 on_move_focus (GtkWidget *widget,
3036 GtkDirectionType direction,
3039 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3043 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3045 GnomeVFSResult result;
3046 GnomeVFSHandle *handle = NULL;
3047 GnomeVFSFileInfo *info = NULL;
3050 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3051 if (result != GNOME_VFS_OK) {
3056 info = gnome_vfs_file_info_new ();
3057 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3058 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3059 /* We put a "safe" default size for going to cache */
3060 *expected_size = (300*1024);
3062 *expected_size = info->size;
3064 gnome_vfs_file_info_unref (info);
3066 stream = tny_vfs_stream_new (handle);
3075 TnyStream *output_stream;
3076 GtkWidget *msg_view;
3080 on_fetch_image_idle_refresh_view (gpointer userdata)
3083 FetchImageData *fidata = (FetchImageData *) userdata;
3084 g_message ("REFRESH VIEW");
3086 gdk_threads_enter ();
3087 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3088 g_message ("QUEUING DRAW");
3089 gtk_widget_queue_draw (fidata->msg_view);
3091 gdk_threads_leave ();
3093 g_object_unref (fidata->msg_view);
3094 g_slice_free (FetchImageData, fidata);
3099 on_fetch_image_thread (gpointer userdata)
3101 FetchImageData *fidata = (FetchImageData *) userdata;
3102 TnyStreamCache *cache;
3103 TnyStream *cache_stream;
3105 cache = modest_runtime_get_images_cache ();
3107 tny_stream_cache_get_stream (cache,
3109 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3110 (gpointer) fidata->uri);
3111 g_free (fidata->cache_id);
3112 g_free (fidata->uri);
3114 if (cache_stream != NULL) {
3117 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3120 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3121 if (G_UNLIKELY (nb_read < 0)) {
3123 } else if (G_LIKELY (nb_read > 0)) {
3124 gssize nb_written = 0;
3126 while (G_UNLIKELY (nb_written < nb_read)) {
3129 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3130 nb_read - nb_written);
3131 if (G_UNLIKELY (len < 0))
3137 tny_stream_close (cache_stream);
3138 g_object_unref (cache_stream);
3141 tny_stream_close (fidata->output_stream);
3142 g_object_unref (fidata->output_stream);
3144 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3150 on_fetch_image (ModestMsgView *msgview,
3153 ModestMsgViewWindow *window)
3155 const gchar *current_account;
3156 ModestMsgViewWindowPrivate *priv;
3157 FetchImageData *fidata;
3159 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3161 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3163 fidata = g_slice_new0 (FetchImageData);
3164 fidata->msg_view = g_object_ref (msgview);
3165 fidata->uri = g_strdup (uri);
3166 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3167 fidata->output_stream = g_object_ref (stream);
3169 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3170 g_object_unref (fidata->output_stream);
3171 g_free (fidata->cache_id);
3172 g_free (fidata->uri);
3173 g_object_unref (fidata->msg_view);
3174 g_slice_free (FetchImageData, fidata);
3175 tny_stream_close (stream);
3183 setup_menu (ModestMsgViewWindow *self)
3185 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3187 /* Settings menu buttons */
3188 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3189 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3190 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3191 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3192 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3193 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3195 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3196 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3197 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3198 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3199 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3200 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3202 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3203 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3204 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3205 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3206 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3207 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3209 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3210 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3211 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3212 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3213 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3214 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3218 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3220 ModestMsgViewWindowPrivate *priv;
3221 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3222 GSList *recipients = NULL;
3224 gboolean contacts_to_add = FALSE;
3226 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3230 header = modest_msg_view_window_get_header (self);
3233 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3234 g_object_unref (header);
3236 recipients = modest_tny_msg_get_all_recipients_list (msg);
3237 g_object_unref (msg);
3240 if (recipients != NULL) {
3241 GtkWidget *picker_dialog;
3242 GtkWidget *selector;
3244 gchar *selected = NULL;
3246 selector = hildon_touch_selector_new_text ();
3247 g_object_ref (selector);
3249 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3250 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3251 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3252 (const gchar *) node->data);
3253 contacts_to_add = TRUE;
3257 if (contacts_to_add) {
3260 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3261 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3263 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3264 HILDON_TOUCH_SELECTOR (selector));
3266 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3268 if (picker_result == GTK_RESPONSE_OK) {
3269 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3271 gtk_widget_destroy (picker_dialog);
3274 modest_address_book_add_address (selected);
3279 g_object_unref (selector);
3284 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3288 _modest_msg_view_window_map_event (GtkWidget *widget,
3292 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3293 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3295 if (priv->progress_hint) {
3296 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), TRUE);