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 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2438 modest_platform_information_banner (NULL, NULL, msg);
2444 /* make the file read-only */
2445 g_chmod(filepath, 0444);
2447 /* Activate the file */
2448 modest_platform_activate_file (filepath, tny_mime_part_get_content_type (mime_part));
2456 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2457 TnyMimePart *mime_part)
2459 ModestMsgViewWindowPrivate *priv;
2460 const gchar *msg_uid;
2461 gchar *attachment_uid = NULL;
2462 gint attachment_index = 0;
2463 TnyList *attachments;
2465 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2466 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2467 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2469 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2470 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2471 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2472 g_object_unref (attachments);
2474 if (msg_uid && attachment_index >= 0) {
2475 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2478 if (mime_part == NULL) {
2479 gboolean error = FALSE;
2480 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2481 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2483 } else if (tny_list_get_length (selected_attachments) > 1) {
2484 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2488 iter = tny_list_create_iterator (selected_attachments);
2489 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2490 g_object_unref (iter);
2492 if (selected_attachments)
2493 g_object_unref (selected_attachments);
2498 g_object_ref (mime_part);
2501 if (tny_mime_part_is_purged (mime_part))
2504 if (!modest_tny_mime_part_is_msg (mime_part)) {
2505 gchar *filepath = NULL;
2506 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2507 gboolean show_error_banner = FALSE;
2508 TnyFsStream *temp_stream = NULL;
2509 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2512 if (temp_stream != NULL) {
2513 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2514 on_decode_to_stream_async_handler,
2516 g_strdup (filepath));
2517 g_object_unref (temp_stream);
2518 /* NOTE: files in the temporary area will be automatically
2519 * cleaned after some time if they are no longer in use */
2522 const gchar *content_type;
2523 /* the file may already exist but it isn't writable,
2524 * let's try to open it anyway */
2525 content_type = tny_mime_part_get_content_type (mime_part);
2526 modest_platform_activate_file (filepath, content_type);
2528 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2529 show_error_banner = TRUE;
2534 if (show_error_banner)
2535 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2537 /* message attachment */
2538 TnyHeader *header = NULL;
2539 ModestWindowMgr *mgr;
2540 ModestWindow *msg_win = NULL;
2543 header = tny_msg_get_header (TNY_MSG (mime_part));
2544 mgr = modest_runtime_get_window_mgr ();
2545 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2548 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2549 * thus, we don't do anything */
2550 g_warning ("window for is already being created");
2552 /* it's not found, so create a new window for it */
2553 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2554 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2555 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2557 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2558 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2559 mailbox, attachment_uid);
2560 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2561 modest_window_get_zoom (MODEST_WINDOW (window)));
2562 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2563 gtk_widget_show_all (GTK_WIDGET (msg_win));
2565 gtk_widget_destroy (GTK_WIDGET (msg_win));
2571 g_free (attachment_uid);
2573 g_object_unref (mime_part);
2585 GnomeVFSResult result;
2588 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2589 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2590 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2591 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2594 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2598 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2599 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2600 g_free (pair->filename);
2601 g_object_unref (pair->part);
2602 g_slice_free (SaveMimePartPair, pair);
2604 g_list_free (info->pairs);
2607 g_slice_free (SaveMimePartInfo, info);
2612 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2614 if (info->pairs != NULL) {
2615 save_mime_part_to_file (info);
2617 /* This is a GDK lock because we are an idle callback and
2618 * hildon_banner_show_information is or does Gtk+ code */
2620 gdk_threads_enter (); /* CHECKED */
2621 save_mime_part_info_free (info, TRUE);
2622 if (info->result == GNOME_VFS_OK) {
2623 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2624 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2625 gchar *msg = g_strdup_printf (_KR("cerm_device_memory_full"), "");
2626 modest_platform_information_banner (NULL, NULL, msg);
2629 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2631 gdk_threads_leave (); /* CHECKED */
2638 save_mime_part_to_file (SaveMimePartInfo *info)
2640 GnomeVFSHandle *handle;
2642 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2644 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2645 if (info->result == GNOME_VFS_OK) {
2646 GError *error = NULL;
2647 stream = tny_vfs_stream_new (handle);
2648 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2649 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2651 if ((error->domain == TNY_ERROR_DOMAIN) &&
2652 (error->code == TNY_IO_ERROR_WRITE) &&
2653 (errno == ENOSPC)) {
2654 info->result = GNOME_VFS_ERROR_NO_SPACE;
2656 info->result = GNOME_VFS_ERROR_IO;
2659 g_object_unref (G_OBJECT (stream));
2660 g_object_unref (pair->part);
2661 g_slice_free (SaveMimePartPair, pair);
2662 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2664 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2665 save_mime_part_info_free (info, FALSE);
2668 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2673 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2675 gboolean is_ok = TRUE;
2676 gint replaced_files = 0;
2677 const GList *files = info->pairs;
2680 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2681 SaveMimePartPair *pair = iter->data;
2682 if (modest_utils_file_exists (pair->filename)) {
2686 if (replaced_files) {
2687 GtkWidget *confirm_overwrite_dialog;
2688 const gchar *message = (replaced_files == 1) ?
2689 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2690 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2691 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2694 gtk_widget_destroy (confirm_overwrite_dialog);
2698 save_mime_part_info_free (info, TRUE);
2700 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2706 save_attachments_response (GtkDialog *dialog,
2710 TnyList *mime_parts;
2712 GList *files_to_save = NULL;
2713 gchar *current_folder;
2715 mime_parts = TNY_LIST (user_data);
2717 if (arg1 != GTK_RESPONSE_OK)
2720 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2721 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2722 if (current_folder && current_folder != '\0') {
2724 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2725 current_folder,&err);
2727 g_debug ("Error storing latest used folder: %s", err->message);
2731 g_free (current_folder);
2733 if (!modest_utils_folder_writable (chooser_uri)) {
2734 hildon_banner_show_information
2735 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2739 iter = tny_list_create_iterator (mime_parts);
2740 while (!tny_iterator_is_done (iter)) {
2741 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2743 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2744 !tny_mime_part_is_purged (mime_part) &&
2745 (tny_mime_part_get_filename (mime_part) != NULL)) {
2746 SaveMimePartPair *pair;
2748 pair = g_slice_new0 (SaveMimePartPair);
2750 if (tny_list_get_length (mime_parts) > 1) {
2752 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2753 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2756 pair->filename = g_strdup (chooser_uri);
2758 pair->part = mime_part;
2759 files_to_save = g_list_prepend (files_to_save, pair);
2761 tny_iterator_next (iter);
2763 g_object_unref (iter);
2765 g_free (chooser_uri);
2767 if (files_to_save != NULL) {
2768 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2769 info->pairs = files_to_save;
2770 info->result = TRUE;
2771 save_mime_parts_to_file_with_checks (info);
2775 /* Free and close the dialog */
2776 g_object_unref (mime_parts);
2777 gtk_widget_destroy (GTK_WIDGET (dialog));
2781 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2782 TnyList *mime_parts)
2784 ModestMsgViewWindowPrivate *priv;
2785 GtkWidget *save_dialog = NULL;
2786 gchar *conf_folder = NULL;
2787 gchar *filename = NULL;
2788 gchar *save_multiple_str = NULL;
2790 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2791 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2793 if (mime_parts == NULL) {
2794 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2795 * selection available */
2796 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2797 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2798 g_object_unref (mime_parts);
2801 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2803 g_object_unref (mime_parts);
2809 g_object_ref (mime_parts);
2812 /* prepare dialog */
2813 if (tny_list_get_length (mime_parts) == 1) {
2815 /* only one attachment selected */
2816 iter = tny_list_create_iterator (mime_parts);
2817 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2818 g_object_unref (iter);
2819 if (!modest_tny_mime_part_is_msg (mime_part) &&
2820 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2821 !tny_mime_part_is_purged (mime_part)) {
2822 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2824 /* TODO: show any error? */
2825 g_warning ("Tried to save a non-file attachment");
2826 g_object_unref (mime_parts);
2829 g_object_unref (mime_part);
2831 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2832 tny_list_get_length (mime_parts));
2835 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2836 GTK_FILE_CHOOSER_ACTION_SAVE);
2839 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2840 if (conf_folder && conf_folder[0] != '\0') {
2841 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2844 /* Set the default folder to images folder */
2845 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2846 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2847 g_free (docs_folder);
2849 g_free (conf_folder);
2853 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2858 /* if multiple, set multiple string */
2859 if (save_multiple_str) {
2860 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2861 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2864 /* We must run this asynchronously, because the hildon dialog
2865 performs a gtk_dialog_run by itself which leads to gdk
2867 g_signal_connect (save_dialog, "response",
2868 G_CALLBACK (save_attachments_response), mime_parts);
2870 gtk_widget_show_all (save_dialog);
2874 show_remove_attachment_information (gpointer userdata)
2876 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2877 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2879 /* We're outside the main lock */
2880 gdk_threads_enter ();
2882 if (priv->remove_attachment_banner != NULL) {
2883 gtk_widget_destroy (priv->remove_attachment_banner);
2884 g_object_unref (priv->remove_attachment_banner);
2887 priv->remove_attachment_banner = g_object_ref (
2888 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2890 gdk_threads_leave ();
2896 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2898 ModestMsgViewWindowPrivate *priv;
2899 TnyList *mime_parts = NULL;
2900 gchar *confirmation_message;
2906 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2907 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2909 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2910 * because we don't have selection
2912 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2914 /* Remove already purged messages from mime parts list */
2915 iter = tny_list_create_iterator (mime_parts);
2916 while (!tny_iterator_is_done (iter)) {
2917 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2918 tny_iterator_next (iter);
2919 if (tny_mime_part_is_purged (part)) {
2920 tny_list_remove (mime_parts, (GObject *) part);
2922 g_object_unref (part);
2924 g_object_unref (iter);
2926 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
2927 tny_list_get_length (mime_parts) == 0) {
2928 g_object_unref (mime_parts);
2932 n_attachments = tny_list_get_length (mime_parts);
2933 if (n_attachments == 1) {
2937 iter = tny_list_create_iterator (mime_parts);
2938 part = (TnyMimePart *) tny_iterator_get_current (iter);
2939 g_object_unref (iter);
2940 if (modest_tny_mime_part_is_msg (part)) {
2942 header = tny_msg_get_header (TNY_MSG (part));
2943 filename = tny_header_dup_subject (header);
2944 g_object_unref (header);
2945 if (filename == NULL)
2946 filename = g_strdup (_("mail_va_no_subject"));
2948 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2950 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2952 g_object_unref (part);
2954 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2955 "mcen_nc_purge_files_text",
2956 n_attachments), n_attachments);
2958 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2959 confirmation_message);
2960 g_free (confirmation_message);
2962 if (response != GTK_RESPONSE_OK) {
2963 g_object_unref (mime_parts);
2967 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2969 iter = tny_list_create_iterator (mime_parts);
2970 while (!tny_iterator_is_done (iter)) {
2973 part = (TnyMimePart *) tny_iterator_get_current (iter);
2974 tny_mime_part_set_purged (TNY_MIME_PART (part));
2975 g_object_unref (part);
2976 tny_iterator_next (iter);
2978 g_object_unref (iter);
2980 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2981 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2982 tny_msg_rewrite_cache (msg);
2983 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2984 g_object_unref (msg);
2986 g_object_unref (mime_parts);
2988 if (priv->purge_timeout > 0) {
2989 g_source_remove (priv->purge_timeout);
2990 priv->purge_timeout = 0;
2993 if (priv->remove_attachment_banner) {
2994 gtk_widget_destroy (priv->remove_attachment_banner);
2995 g_object_unref (priv->remove_attachment_banner);
2996 priv->remove_attachment_banner = NULL;
3004 update_window_title (ModestMsgViewWindow *window)
3006 ModestMsgViewWindowPrivate *priv;
3008 TnyHeader *header = NULL;
3009 gchar *subject = NULL;
3011 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3013 /* Note that if the window is closed while we're retrieving
3014 the message, this widget could de deleted */
3015 if (!priv->msg_view)
3018 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3021 header = tny_msg_get_header (msg);
3022 subject = tny_header_dup_subject (header);
3023 g_object_unref (header);
3024 g_object_unref (msg);
3027 if ((subject == NULL)||(subject[0] == '\0')) {
3029 subject = g_strdup (_("mail_va_no_subject"));
3032 gtk_window_set_title (GTK_WINDOW (window), subject);
3037 on_move_focus (GtkWidget *widget,
3038 GtkDirectionType direction,
3041 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3045 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3047 GnomeVFSResult result;
3048 GnomeVFSHandle *handle = NULL;
3049 GnomeVFSFileInfo *info = NULL;
3052 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3053 if (result != GNOME_VFS_OK) {
3058 info = gnome_vfs_file_info_new ();
3059 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3060 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3061 /* We put a "safe" default size for going to cache */
3062 *expected_size = (300*1024);
3064 *expected_size = info->size;
3066 gnome_vfs_file_info_unref (info);
3068 stream = tny_vfs_stream_new (handle);
3077 TnyStream *output_stream;
3078 GtkWidget *msg_view;
3082 on_fetch_image_idle_refresh_view (gpointer userdata)
3085 FetchImageData *fidata = (FetchImageData *) userdata;
3086 g_message ("REFRESH VIEW");
3088 gdk_threads_enter ();
3089 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3090 g_message ("QUEUING DRAW");
3091 gtk_widget_queue_draw (fidata->msg_view);
3093 gdk_threads_leave ();
3095 g_object_unref (fidata->msg_view);
3096 g_slice_free (FetchImageData, fidata);
3101 on_fetch_image_thread (gpointer userdata)
3103 FetchImageData *fidata = (FetchImageData *) userdata;
3104 TnyStreamCache *cache;
3105 TnyStream *cache_stream;
3107 cache = modest_runtime_get_images_cache ();
3109 tny_stream_cache_get_stream (cache,
3111 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3112 (gpointer) fidata->uri);
3113 g_free (fidata->cache_id);
3114 g_free (fidata->uri);
3116 if (cache_stream != NULL) {
3119 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3122 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3123 if (G_UNLIKELY (nb_read < 0)) {
3125 } else if (G_LIKELY (nb_read > 0)) {
3126 gssize nb_written = 0;
3128 while (G_UNLIKELY (nb_written < nb_read)) {
3131 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3132 nb_read - nb_written);
3133 if (G_UNLIKELY (len < 0))
3139 tny_stream_close (cache_stream);
3140 g_object_unref (cache_stream);
3143 tny_stream_close (fidata->output_stream);
3144 g_object_unref (fidata->output_stream);
3146 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3152 on_fetch_image (ModestMsgView *msgview,
3155 ModestMsgViewWindow *window)
3157 const gchar *current_account;
3158 ModestMsgViewWindowPrivate *priv;
3159 FetchImageData *fidata;
3161 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3163 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3165 fidata = g_slice_new0 (FetchImageData);
3166 fidata->msg_view = g_object_ref (msgview);
3167 fidata->uri = g_strdup (uri);
3168 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3169 fidata->output_stream = g_object_ref (stream);
3171 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3172 g_object_unref (fidata->output_stream);
3173 g_free (fidata->cache_id);
3174 g_free (fidata->uri);
3175 g_object_unref (fidata->msg_view);
3176 g_slice_free (FetchImageData, fidata);
3177 tny_stream_close (stream);
3185 setup_menu (ModestMsgViewWindow *self)
3187 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3189 /* Settings menu buttons */
3190 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3191 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3192 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3193 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3194 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3195 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3197 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3198 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3199 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3200 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3201 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3202 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3204 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3205 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3206 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3207 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3208 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3209 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3211 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3212 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3213 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3214 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3215 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3216 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3220 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3222 ModestMsgViewWindowPrivate *priv;
3223 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3224 GSList *recipients = NULL;
3226 gboolean contacts_to_add = FALSE;
3228 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3232 header = modest_msg_view_window_get_header (self);
3235 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3236 g_object_unref (header);
3238 recipients = modest_tny_msg_get_all_recipients_list (msg);
3239 g_object_unref (msg);
3242 if (recipients != NULL) {
3243 GtkWidget *picker_dialog;
3244 GtkWidget *selector;
3246 gchar *selected = NULL;
3248 selector = hildon_touch_selector_new_text ();
3249 g_object_ref (selector);
3251 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3252 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3253 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3254 (const gchar *) node->data);
3255 contacts_to_add = TRUE;
3259 if (contacts_to_add) {
3262 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3263 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3265 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3266 HILDON_TOUCH_SELECTOR (selector));
3268 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3270 if (picker_result == GTK_RESPONSE_OK) {
3271 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3273 gtk_widget_destroy (picker_dialog);
3276 modest_address_book_add_address (selected);
3281 g_object_unref (selector);
3286 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3290 _modest_msg_view_window_map_event (GtkWidget *widget,
3294 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3295 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3297 if (priv->progress_hint) {
3298 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), TRUE);