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 "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar.h"
51 #include "hildon/hildon-pannable-area.h"
52 #include "modest-defs.h"
53 #include "modest-hildon-includes.h"
54 #include "modest-ui-dimming-manager.h"
55 #include <gdk/gdkkeysyms.h>
56 #include <modest-tny-account.h>
57 #include <modest-mime-part-view.h>
58 #include <modest-isearch-view.h>
59 #include <modest-tny-mime-part.h>
62 #include <glib/gstdio.h>
63 #include <modest-debug.h>
65 #define DEFAULT_FOLDER "MyDocs/.documents"
67 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
68 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
69 static void modest_header_view_observer_init(
70 ModestHeaderViewObserverIface *iface_class);
71 static void modest_msg_view_window_finalize (GObject *obj);
72 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
74 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
75 ModestMsgViewWindow *obj);
76 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
77 ModestMsgViewWindow *obj);
79 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
81 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
82 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
86 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
88 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
89 gboolean show_toolbar);
91 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
93 ModestMsgViewWindow *window);
95 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
98 ModestMsgViewWindow *window);
100 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
102 ModestMsgViewWindow *window);
104 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
105 GtkTreePath *tree_path,
106 GtkTreeIter *tree_iter,
107 ModestMsgViewWindow *window);
109 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
113 ModestMsgViewWindow *window);
115 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
117 const gchar *tny_folder_id);
119 static void cancel_progressbar (GtkToolButton *toolbutton,
120 ModestMsgViewWindow *self);
122 static void on_queue_changed (ModestMailOperationQueue *queue,
123 ModestMailOperation *mail_op,
124 ModestMailOperationQueueNotification type,
125 ModestMsgViewWindow *self);
127 static void on_account_removed (TnyAccountStore *account_store,
131 static void on_move_focus (GtkWidget *widget,
132 GtkDirectionType direction,
135 static void view_msg_cb (ModestMailOperation *mail_op,
142 static void set_toolbar_mode (ModestMsgViewWindow *self,
143 ModestToolBarModes mode);
145 static void update_window_title (ModestMsgViewWindow *window);
147 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
148 static void init_window (ModestMsgViewWindow *obj);
150 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
152 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
154 static gboolean on_fetch_image (ModestMsgView *msgview,
157 ModestMsgViewWindow *window);
159 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
160 GtkScrollType scroll_type,
164 /* list my signals */
171 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
172 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
175 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
176 struct _ModestMsgViewWindowPrivate {
179 GtkWidget *main_scroll;
180 GtkWidget *find_toolbar;
183 /* Progress observers */
184 GtkWidget *progress_bar;
185 GSList *progress_widgets;
188 GtkWidget *progress_toolitem;
189 GtkWidget *cancel_toolitem;
190 GtkWidget *prev_toolitem;
191 GtkWidget *next_toolitem;
192 ModestToolBarModes current_toolbar_mode;
194 /* Optimized view enabled */
195 gboolean optimized_view;
197 /* Whether this was created via the *_new_for_search_result() function. */
198 gboolean is_search_result;
200 /* Whether the message is in outbox */
203 /* A reference to the @model of the header view
204 * to allow selecting previous/next messages,
205 * if the message is currently selected in the header view.
207 const gchar *header_folder_id;
208 GtkTreeModel *header_model;
209 GtkTreeRowReference *row_reference;
210 GtkTreeRowReference *next_row_reference;
212 gulong clipboard_change_handler;
213 gulong queue_change_handler;
214 gulong account_removed_handler;
215 gulong row_changed_handler;
216 gulong row_deleted_handler;
217 gulong row_inserted_handler;
218 gulong rows_reordered_handler;
221 GtkWidget *remove_attachment_banner;
223 guint progress_bar_timeout;
230 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
231 MODEST_TYPE_MSG_VIEW_WINDOW, \
232 ModestMsgViewWindowPrivate))
234 static GtkWindowClass *parent_class = NULL;
236 /* uncomment the following if you have defined any signals */
237 static guint signals[LAST_SIGNAL] = {0};
240 modest_msg_view_window_get_type (void)
242 static GType my_type = 0;
244 static const GTypeInfo my_info = {
245 sizeof(ModestMsgViewWindowClass),
246 NULL, /* base init */
247 NULL, /* base finalize */
248 (GClassInitFunc) modest_msg_view_window_class_init,
249 NULL, /* class finalize */
250 NULL, /* class data */
251 sizeof(ModestMsgViewWindow),
253 (GInstanceInitFunc) modest_msg_view_window_init,
256 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
257 "ModestMsgViewWindow",
260 static const GInterfaceInfo modest_header_view_observer_info =
262 (GInterfaceInitFunc) modest_header_view_observer_init,
263 NULL, /* interface_finalize */
264 NULL /* interface_data */
267 g_type_add_interface_static (my_type,
268 MODEST_TYPE_HEADER_VIEW_OBSERVER,
269 &modest_header_view_observer_info);
275 save_state (ModestWindow *self)
277 modest_widget_memory_save (modest_runtime_get_conf (),
279 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
283 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
284 GtkScrollType scroll_type,
288 ModestMsgViewWindowPrivate *priv;
289 gboolean return_value;
291 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
292 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
297 add_scroll_binding (GtkBindingSet *binding_set,
299 GtkScrollType scroll)
301 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
303 gtk_binding_entry_add_signal (binding_set, keyval, 0,
305 GTK_TYPE_SCROLL_TYPE, scroll,
306 G_TYPE_BOOLEAN, FALSE);
307 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
309 GTK_TYPE_SCROLL_TYPE, scroll,
310 G_TYPE_BOOLEAN, FALSE);
314 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
316 GObjectClass *gobject_class;
317 ModestWindowClass *modest_window_class;
318 GtkBindingSet *binding_set;
320 gobject_class = (GObjectClass*) klass;
321 modest_window_class = (ModestWindowClass *) klass;
323 parent_class = g_type_class_peek_parent (klass);
324 gobject_class->finalize = modest_msg_view_window_finalize;
326 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
327 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
328 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
330 modest_window_class->save_state_func = save_state;
332 klass->scroll_child = modest_msg_view_window_scroll_child;
334 signals[MSG_CHANGED_SIGNAL] =
335 g_signal_new ("msg-changed",
336 G_TYPE_FROM_CLASS (gobject_class),
338 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
340 modest_marshal_VOID__POINTER_POINTER,
341 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
343 signals[SCROLL_CHILD_SIGNAL] =
344 g_signal_new ("scroll-child",
345 G_TYPE_FROM_CLASS (gobject_class),
346 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
347 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
349 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
350 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
352 binding_set = gtk_binding_set_by_class (klass);
353 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
354 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
355 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
356 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
357 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
358 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
360 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
364 static void modest_header_view_observer_init(
365 ModestHeaderViewObserverIface *iface_class)
367 iface_class->update_func = modest_msg_view_window_update_model_replaced;
371 modest_msg_view_window_init (ModestMsgViewWindow *obj)
373 ModestMsgViewWindowPrivate *priv;
374 ModestWindowPrivate *parent_priv = NULL;
375 GtkActionGroup *action_group = NULL;
376 GError *error = NULL;
377 GdkPixbuf *window_icon;
379 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
380 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
381 parent_priv->ui_manager = gtk_ui_manager_new();
383 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
384 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
386 /* Add common actions */
387 gtk_action_group_add_actions (action_group,
388 modest_action_entries,
389 G_N_ELEMENTS (modest_action_entries),
391 gtk_action_group_add_toggle_actions (action_group,
392 msg_view_toggle_action_entries,
393 G_N_ELEMENTS (msg_view_toggle_action_entries),
396 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
397 g_object_unref (action_group);
399 /* Load the UI definition */
400 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
403 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
404 g_error_free (error);
409 /* Add accelerators */
410 gtk_window_add_accel_group (GTK_WINDOW (obj),
411 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
413 priv->is_search_result = FALSE;
414 priv->is_outbox = FALSE;
416 priv->msg_view = NULL;
417 priv->header_model = NULL;
418 priv->header_folder_id = NULL;
419 priv->clipboard_change_handler = 0;
420 priv->queue_change_handler = 0;
421 priv->account_removed_handler = 0;
422 priv->row_changed_handler = 0;
423 priv->row_deleted_handler = 0;
424 priv->row_inserted_handler = 0;
425 priv->rows_reordered_handler = 0;
426 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
428 priv->optimized_view = FALSE;
429 priv->progress_bar_timeout = 0;
430 priv->purge_timeout = 0;
431 priv->remove_attachment_banner = NULL;
432 priv->msg_uid = NULL;
434 priv->sighandlers = NULL;
437 init_window (MODEST_MSG_VIEW_WINDOW(obj));
439 /* Set window icon */
440 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
442 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
443 g_object_unref (window_icon);
446 hildon_program_add_window (hildon_program_get_instance(),
453 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
455 ModestMsgViewWindowPrivate *priv = NULL;
457 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
459 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
461 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
463 if (priv->progress_bar_timeout > 0) {
464 g_source_remove (priv->progress_bar_timeout);
465 priv->progress_bar_timeout = 0;
472 set_toolbar_mode (ModestMsgViewWindow *self,
473 ModestToolBarModes mode)
475 ModestWindowPrivate *parent_priv;
476 ModestMsgViewWindowPrivate *priv;
477 /* GtkWidget *widget = NULL; */
479 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
481 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
482 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
484 /* Sets current toolbar mode */
485 priv->current_toolbar_mode = mode;
487 /* Update toolbar dimming state */
488 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
491 case TOOLBAR_MODE_NORMAL:
492 if (priv->progress_toolitem) {
493 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
494 gtk_widget_hide (priv->progress_toolitem);
497 if (priv->progress_bar)
498 gtk_widget_hide (priv->progress_bar);
500 if (priv->cancel_toolitem)
501 gtk_widget_hide (priv->cancel_toolitem);
503 if (priv->prev_toolitem)
504 gtk_widget_show (priv->prev_toolitem);
506 if (priv->next_toolitem)
507 gtk_widget_show (priv->next_toolitem);
509 /* Hide toolbar if optimized view is enabled */
510 if (priv->optimized_view) {
511 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
512 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
516 case TOOLBAR_MODE_TRANSFER:
517 if (priv->prev_toolitem)
518 gtk_widget_hide (priv->prev_toolitem);
520 if (priv->next_toolitem)
521 gtk_widget_hide (priv->next_toolitem);
523 if (priv->progress_bar)
524 gtk_widget_show (priv->progress_bar);
526 if (priv->progress_toolitem) {
527 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
528 gtk_widget_show (priv->progress_toolitem);
531 if (priv->cancel_toolitem)
532 gtk_widget_show (priv->cancel_toolitem);
534 /* Show toolbar if it's hiden (optimized view ) */
535 if (priv->optimized_view) {
536 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
537 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
542 g_return_if_reached ();
549 init_window (ModestMsgViewWindow *obj)
551 GtkWidget *main_vbox;
552 ModestMsgViewWindowPrivate *priv;
554 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
556 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
557 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
558 main_vbox = gtk_vbox_new (FALSE, 6);
559 #ifdef MODEST_TOOLKIT_HILDON2
560 priv->main_scroll = hildon_pannable_area_new ();
561 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
563 #ifdef MODEST_USE_MOZEMBED
564 priv->main_scroll = priv->msg_view;
565 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
567 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
568 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
570 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
571 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
572 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
575 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
576 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
578 priv->find_toolbar = hildon_find_toolbar_new (NULL);
579 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
580 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
582 gtk_widget_show_all (GTK_WIDGET(main_vbox));
586 modest_msg_view_window_disconnect_signals (ModestWindow *self)
588 ModestMsgViewWindowPrivate *priv;
589 ModestHeaderView *header_view = NULL;
590 ModestWindow *main_window = NULL;
592 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
594 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
595 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
596 priv->clipboard_change_handler))
597 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
598 priv->clipboard_change_handler);
600 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
601 priv->queue_change_handler))
602 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
603 priv->queue_change_handler);
605 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
606 priv->account_removed_handler))
607 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
608 priv->account_removed_handler);
610 if (priv->header_model) {
611 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
612 priv->row_changed_handler))
613 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
614 priv->row_changed_handler);
616 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
617 priv->row_deleted_handler))
618 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
619 priv->row_deleted_handler);
621 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
622 priv->row_inserted_handler))
623 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
624 priv->row_inserted_handler);
626 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
627 priv->rows_reordered_handler))
628 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
629 priv->rows_reordered_handler);
632 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
633 priv->sighandlers = NULL;
635 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
636 FALSE); /* don't create */
640 header_view = MODEST_HEADER_VIEW(
641 modest_main_window_get_child_widget(
642 MODEST_MAIN_WINDOW(main_window),
643 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
644 if (header_view == NULL)
647 modest_header_view_remove_observer(header_view,
648 MODEST_HEADER_VIEW_OBSERVER(self));
652 modest_msg_view_window_finalize (GObject *obj)
654 ModestMsgViewWindowPrivate *priv;
656 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
658 /* Sanity check: shouldn't be needed, the window mgr should
659 call this function before */
660 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
662 if (priv->header_model != NULL) {
663 g_object_unref (priv->header_model);
664 priv->header_model = NULL;
667 if (priv->progress_bar_timeout > 0) {
668 g_source_remove (priv->progress_bar_timeout);
669 priv->progress_bar_timeout = 0;
672 if (priv->remove_attachment_banner) {
673 gtk_widget_destroy (priv->remove_attachment_banner);
674 g_object_unref (priv->remove_attachment_banner);
675 priv->remove_attachment_banner = NULL;
678 if (priv->purge_timeout > 0) {
679 g_source_remove (priv->purge_timeout);
680 priv->purge_timeout = 0;
683 if (priv->row_reference) {
684 gtk_tree_row_reference_free (priv->row_reference);
685 priv->row_reference = NULL;
688 if (priv->next_row_reference) {
689 gtk_tree_row_reference_free (priv->next_row_reference);
690 priv->next_row_reference = NULL;
694 g_free (priv->msg_uid);
695 priv->msg_uid = NULL;
698 G_OBJECT_CLASS(parent_class)->finalize (obj);
702 select_next_valid_row (GtkTreeModel *model,
703 GtkTreeRowReference **row_reference,
707 GtkTreeIter tmp_iter;
709 GtkTreePath *next = NULL;
710 gboolean retval = FALSE, finished;
712 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
714 path = gtk_tree_row_reference_get_path (*row_reference);
715 gtk_tree_model_get_iter (model, &tmp_iter, path);
716 gtk_tree_row_reference_free (*row_reference);
717 *row_reference = NULL;
721 TnyHeader *header = NULL;
723 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
724 gtk_tree_model_get (model, &tmp_iter,
725 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
729 if (msg_is_visible (header, is_outbox)) {
730 next = gtk_tree_model_get_path (model, &tmp_iter);
731 *row_reference = gtk_tree_row_reference_new (model, next);
732 gtk_tree_path_free (next);
736 g_object_unref (header);
739 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
740 next = gtk_tree_model_get_path (model, &tmp_iter);
742 /* Ensure that we are not selecting the same */
743 if (gtk_tree_path_compare (path, next) != 0) {
744 gtk_tree_model_get (model, &tmp_iter,
745 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
748 if (msg_is_visible (header, is_outbox)) {
749 *row_reference = gtk_tree_row_reference_new (model, next);
753 g_object_unref (header);
757 /* If we ended up in the same message
758 then there is no valid next
762 gtk_tree_path_free (next);
764 /* If there are no more messages and we don't
765 want to start again in the first one then
766 there is no valid next message */
772 gtk_tree_path_free (path);
777 /* TODO: This should be in _init(), with the parameters as properties. */
779 modest_msg_view_window_construct (ModestMsgViewWindow *self,
780 const gchar *modest_account_name,
781 const gchar *msg_uid)
784 ModestMsgViewWindowPrivate *priv = NULL;
785 ModestWindowPrivate *parent_priv = NULL;
786 ModestDimmingRulesGroup *menu_rules_group = NULL;
787 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
788 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
790 obj = G_OBJECT (self);
791 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
792 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
794 priv->msg_uid = g_strdup (msg_uid);
797 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
798 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
799 gtk_widget_show (parent_priv->menubar);
800 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
802 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
803 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
804 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
806 /* Add common dimming rules */
807 modest_dimming_rules_group_add_rules (menu_rules_group,
808 modest_msg_view_menu_dimming_entries,
809 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
810 MODEST_WINDOW (self));
811 modest_dimming_rules_group_add_rules (toolbar_rules_group,
812 modest_msg_view_toolbar_dimming_entries,
813 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
814 MODEST_WINDOW (self));
815 modest_dimming_rules_group_add_rules (clipboard_rules_group,
816 modest_msg_view_clipboard_dimming_entries,
817 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
818 MODEST_WINDOW (self));
820 /* Insert dimming rules group for this window */
821 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
822 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
823 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
824 g_object_unref (menu_rules_group);
825 g_object_unref (toolbar_rules_group);
826 g_object_unref (clipboard_rules_group);
828 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
830 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);
831 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
832 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
833 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
834 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
835 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
836 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
837 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
838 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
839 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
840 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
841 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
842 G_CALLBACK (on_fetch_image), obj);
844 g_signal_connect (G_OBJECT (obj), "key-release-event",
845 G_CALLBACK (modest_msg_view_window_key_event),
848 g_signal_connect (G_OBJECT (obj), "key-press-event",
849 G_CALLBACK (modest_msg_view_window_key_event),
852 g_signal_connect (G_OBJECT (obj), "move-focus",
853 G_CALLBACK (on_move_focus), obj);
855 /* Mail Operation Queue */
856 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
858 G_CALLBACK (on_queue_changed),
861 /* Account manager */
862 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
864 G_CALLBACK(on_account_removed),
867 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
869 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
870 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
871 priv->last_search = NULL;
873 /* Init the clipboard actions dim status */
874 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
876 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
881 /* FIXME: parameter checks */
883 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
884 const gchar *modest_account_name,
885 const gchar *msg_uid,
887 GtkTreeRowReference *row_reference)
889 ModestMsgViewWindow *window = NULL;
890 ModestMsgViewWindowPrivate *priv = NULL;
891 TnyFolder *header_folder = NULL;
892 ModestHeaderView *header_view = NULL;
893 ModestWindow *main_window = NULL;
894 ModestWindowMgr *mgr = NULL;
897 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
900 mgr = modest_runtime_get_window_mgr ();
901 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
902 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
904 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
906 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
908 /* Remember the message list's TreeModel so we can detect changes
909 * and change the list selection when necessary: */
911 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
913 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
914 MODEST_MAIN_WINDOW(main_window),
915 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
918 if (header_view != NULL){
919 header_folder = modest_header_view_get_folder(header_view);
920 /* This could happen if the header folder was
921 unseleted before opening this msg window (for
922 example if the user selects an account in the
923 folder view of the main window */
925 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
926 priv->header_folder_id = tny_folder_get_id(header_folder);
927 g_assert(priv->header_folder_id != NULL);
928 g_object_unref(header_folder);
932 /* Setup row references and connect signals */
933 priv->header_model = g_object_ref (model);
936 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
937 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
938 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
940 priv->row_reference = NULL;
941 priv->next_row_reference = NULL;
944 /* Connect signals */
945 priv->row_changed_handler =
946 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
947 G_CALLBACK(modest_msg_view_window_on_row_changed),
949 priv->row_deleted_handler =
950 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
951 G_CALLBACK(modest_msg_view_window_on_row_deleted),
953 priv->row_inserted_handler =
954 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
955 G_CALLBACK(modest_msg_view_window_on_row_inserted),
957 priv->rows_reordered_handler =
958 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
959 G_CALLBACK(modest_msg_view_window_on_row_reordered),
962 if (header_view != NULL){
963 modest_header_view_add_observer(header_view,
964 MODEST_HEADER_VIEW_OBSERVER(window));
967 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
968 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
969 /* gtk_widget_show_all (GTK_WIDGET (window)); */
970 modest_msg_view_window_update_priority (window);
972 /* Check dimming rules */
973 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
974 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
975 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
977 return MODEST_WINDOW(window);
981 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
982 const gchar *modest_account_name,
983 const gchar *msg_uid)
985 ModestMsgViewWindow *window = NULL;
986 ModestMsgViewWindowPrivate *priv = NULL;
987 ModestWindowMgr *mgr = NULL;
989 mgr = modest_runtime_get_window_mgr ();
990 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
991 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
992 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
994 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
996 /* Remember that this is a search result,
997 * so we can disable some UI appropriately: */
998 priv->is_search_result = TRUE;
1000 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1002 update_window_title (window);
1003 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1004 modest_msg_view_window_update_priority (window);
1006 /* Check dimming rules */
1007 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1008 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1009 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1011 return MODEST_WINDOW(window);
1015 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1016 const gchar *modest_account_name,
1017 const gchar *msg_uid)
1019 GObject *obj = NULL;
1020 ModestMsgViewWindowPrivate *priv;
1021 ModestWindowMgr *mgr = NULL;
1023 g_return_val_if_fail (msg, NULL);
1024 mgr = modest_runtime_get_window_mgr ();
1025 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1026 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1027 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1028 modest_account_name, msg_uid);
1030 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1031 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1033 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1035 /* Check dimming rules */
1036 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1037 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1038 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1040 return MODEST_WINDOW(obj);
1044 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1047 ModestMsgViewWindow *window)
1049 check_dimming_rules_after_change (window);
1053 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1055 ModestMsgViewWindow *window)
1057 check_dimming_rules_after_change (window);
1059 /* The window could have dissapeared */
1062 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1064 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1065 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1069 /* On insertions we check if the folder still has the message we are
1070 * showing or do not. If do not, we do nothing. Which means we are still
1071 * not attached to any header folder and thus next/prev buttons are
1072 * still dimmed. Once the message that is shown by msg-view is found, the
1073 * new model of header-view will be attached and the references will be set.
1074 * On each further insertions dimming rules will be checked. However
1075 * this requires extra CPU time at least works.
1076 * (An message might be deleted from TnyFolder and thus will not be
1077 * inserted into the model again for example if it is removed by the
1078 * imap server and the header view is refreshed.)
1081 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1082 GtkTreePath *tree_path,
1083 GtkTreeIter *tree_iter,
1084 ModestMsgViewWindow *window)
1086 ModestMsgViewWindowPrivate *priv = NULL;
1087 TnyHeader *header = NULL;
1089 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1090 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1092 g_assert (model == priv->header_model);
1094 /* Check if the newly inserted message is the same we are actually
1095 * showing. IF not, we should remain detached from the header model
1096 * and thus prev and next toolbar buttons should remain dimmed. */
1097 gtk_tree_model_get (model, tree_iter,
1098 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1101 if (TNY_IS_HEADER (header)) {
1104 uid = modest_tny_folder_get_header_unique_id (header);
1105 if (!g_str_equal(priv->msg_uid, uid)) {
1106 check_dimming_rules_after_change (window);
1108 g_object_unref (G_OBJECT(header));
1112 g_object_unref(G_OBJECT(header));
1115 if (priv->row_reference) {
1116 gtk_tree_row_reference_free (priv->row_reference);
1119 /* Setup row_reference for the actual msg. */
1120 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1121 if (priv->row_reference == NULL) {
1122 g_warning("No reference for msg header item.");
1126 /* Now set up next_row_reference. */
1127 if (priv->next_row_reference) {
1128 gtk_tree_row_reference_free (priv->next_row_reference);
1131 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1132 select_next_valid_row (priv->header_model,
1133 &(priv->next_row_reference), FALSE, priv->is_outbox);
1135 /* Connect the remaining callbacks to become able to detect
1136 * changes in header-view. */
1137 priv->row_changed_handler =
1138 g_signal_connect (priv->header_model, "row-changed",
1139 G_CALLBACK (modest_msg_view_window_on_row_changed),
1141 priv->row_deleted_handler =
1142 g_signal_connect (priv->header_model, "row-deleted",
1143 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1145 priv->rows_reordered_handler =
1146 g_signal_connect (priv->header_model, "rows-reordered",
1147 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1150 check_dimming_rules_after_change (window);
1154 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1158 ModestMsgViewWindow *window)
1160 ModestMsgViewWindowPrivate *priv = NULL;
1161 gboolean already_changed = FALSE;
1163 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1165 /* If the current row was reordered select the proper next
1166 valid row. The same if the next row reference changes */
1167 if (priv->row_reference &&
1168 gtk_tree_row_reference_valid (priv->row_reference)) {
1170 path = gtk_tree_row_reference_get_path (priv->row_reference);
1171 if (gtk_tree_path_compare (path, arg1) == 0) {
1172 if (priv->next_row_reference) {
1173 gtk_tree_row_reference_free (priv->next_row_reference);
1175 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1176 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1177 already_changed = TRUE;
1179 gtk_tree_path_free (path);
1181 if (!already_changed &&
1182 priv->next_row_reference &&
1183 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1185 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1186 if (gtk_tree_path_compare (path, arg1) == 0) {
1187 if (priv->next_row_reference) {
1188 gtk_tree_row_reference_free (priv->next_row_reference);
1190 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1191 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1193 gtk_tree_path_free (path);
1195 check_dimming_rules_after_change (window);
1198 /* The modest_msg_view_window_update_model_replaced implements update
1199 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1200 * actually belongs to the header-view is the same as the TnyFolder of
1201 * the message of msg-view or not. If they are different, there is
1202 * nothing to do. If they are the same, then the model has replaced and
1203 * the reference in msg-view shall be replaced from the old model to
1204 * the new model. In this case the view will be detached from it's
1205 * header folder. From this point the next/prev buttons are dimmed.
1208 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1209 GtkTreeModel *model,
1210 const gchar *tny_folder_id)
1212 ModestMsgViewWindowPrivate *priv = NULL;
1213 ModestMsgViewWindow *window = NULL;
1215 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1216 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1218 window = MODEST_MSG_VIEW_WINDOW(observer);
1219 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1221 /* If there is an other folder in the header-view then we do
1222 * not care about it's model (msg list). Else if the
1223 * header-view shows the folder the msg shown by us is in, we
1224 * shall replace our model reference and make some check. */
1225 if(model == NULL || tny_folder_id == NULL ||
1226 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1229 /* Model is changed(replaced), so we should forget the old
1230 * one. Because there might be other references and there
1231 * might be some change on the model even if we unreferenced
1232 * it, we need to disconnect our signals here. */
1233 if (priv->header_model) {
1234 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1235 priv->row_changed_handler))
1236 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1237 priv->row_changed_handler);
1238 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1239 priv->row_deleted_handler))
1240 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1241 priv->row_deleted_handler);
1242 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1243 priv->row_inserted_handler))
1244 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1245 priv->row_inserted_handler);
1246 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1247 priv->rows_reordered_handler))
1248 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1249 priv->rows_reordered_handler);
1252 if (priv->row_reference)
1253 gtk_tree_row_reference_free (priv->row_reference);
1254 if (priv->next_row_reference)
1255 gtk_tree_row_reference_free (priv->next_row_reference);
1256 g_object_unref(priv->header_model);
1259 priv->row_changed_handler = 0;
1260 priv->row_deleted_handler = 0;
1261 priv->row_inserted_handler = 0;
1262 priv->rows_reordered_handler = 0;
1263 priv->next_row_reference = NULL;
1264 priv->row_reference = NULL;
1265 priv->header_model = NULL;
1268 priv->header_model = g_object_ref (model);
1270 /* Also we must connect to the new model for row insertions.
1271 * Only for insertions now. We will need other ones only after
1272 * the msg is show by msg-view is added to the new model. */
1273 priv->row_inserted_handler =
1274 g_signal_connect (priv->header_model, "row-inserted",
1275 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1278 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1279 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1283 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1285 ModestMsgViewWindowPrivate *priv= NULL;
1287 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1288 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1290 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1294 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1296 ModestMsgViewWindowPrivate *priv= NULL;
1298 TnyHeader *header = NULL;
1299 GtkTreePath *path = NULL;
1302 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1303 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1305 /* If the message was not obtained from a treemodel,
1306 * for instance if it was opened directly by the search UI:
1308 if (priv->header_model == NULL ||
1309 priv->row_reference == NULL ||
1310 !gtk_tree_row_reference_valid (priv->row_reference)) {
1311 msg = modest_msg_view_window_get_message (self);
1313 header = tny_msg_get_header (msg);
1314 g_object_unref (msg);
1319 /* Get iter of the currently selected message in the header view: */
1320 path = gtk_tree_row_reference_get_path (priv->row_reference);
1321 g_return_val_if_fail (path != NULL, NULL);
1322 gtk_tree_model_get_iter (priv->header_model,
1326 /* Get current message header */
1327 gtk_tree_model_get (priv->header_model, &iter,
1328 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1331 gtk_tree_path_free (path);
1336 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1338 ModestMsgViewWindowPrivate *priv;
1340 g_return_val_if_fail (self, NULL);
1342 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1344 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1348 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1350 ModestMsgViewWindowPrivate *priv;
1352 g_return_val_if_fail (self, NULL);
1354 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1356 return (const gchar*) priv->msg_uid;
1360 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1363 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1364 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1365 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1369 is_active = gtk_toggle_action_get_active (toggle);
1372 gtk_widget_show (priv->find_toolbar);
1373 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1375 gtk_widget_hide (priv->find_toolbar);
1376 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1379 /* update the toggle buttons status */
1380 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1381 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1385 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1386 ModestMsgViewWindow *obj)
1388 GtkToggleAction *toggle;
1389 ModestWindowPrivate *parent_priv;
1390 ModestMsgViewWindowPrivate *priv;
1392 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1393 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1395 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1396 gtk_toggle_action_set_active (toggle, FALSE);
1397 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1401 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1402 ModestMsgViewWindow *obj)
1404 gchar *current_search;
1405 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1407 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1408 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1412 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1414 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1415 g_free (current_search);
1416 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1420 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1422 g_free (priv->last_search);
1423 priv->last_search = g_strdup (current_search);
1424 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1427 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1428 g_free (priv->last_search);
1429 priv->last_search = NULL;
1431 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1432 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1435 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1436 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1437 g_free (priv->last_search);
1438 priv->last_search = NULL;
1440 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1441 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1445 g_free (current_search);
1450 modest_msg_view_window_get_zoom (ModestWindow *window)
1452 ModestMsgViewWindowPrivate *priv;
1454 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1456 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1457 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1461 modest_msg_view_window_key_event (GtkWidget *window,
1467 focus = gtk_window_get_focus (GTK_WINDOW (window));
1469 /* for the find toolbar case */
1470 if (focus && GTK_IS_ENTRY (focus)) {
1471 if (event->keyval == GDK_BackSpace) {
1473 copy = gdk_event_copy ((GdkEvent *) event);
1474 gtk_widget_event (focus, copy);
1475 gdk_event_free (copy);
1480 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1481 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1482 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1483 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1484 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1485 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1486 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1487 /* gboolean return_value; */
1489 if (event->type == GDK_KEY_PRESS) {
1490 GtkScrollType scroll_type;
1492 switch (event->keyval) {
1495 scroll_type = GTK_SCROLL_STEP_UP; break;
1498 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1500 case GDK_KP_Page_Up:
1501 scroll_type = GTK_SCROLL_PAGE_UP; break;
1503 case GDK_KP_Page_Down:
1504 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1507 scroll_type = GTK_SCROLL_START; break;
1510 scroll_type = GTK_SCROLL_END; break;
1511 default: scroll_type = GTK_SCROLL_NONE;
1514 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1515 /* scroll_type, FALSE, &return_value); */
1526 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1529 ModestMsgViewWindowPrivate *priv;
1530 GtkTreeIter tmp_iter;
1531 gboolean is_last_selected;
1533 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1534 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1536 /*if no model (so no rows at all), then virtually we are the last*/
1537 if (!priv->header_model || !priv->row_reference)
1540 if (!gtk_tree_row_reference_valid (priv->row_reference))
1543 path = gtk_tree_row_reference_get_path (priv->row_reference);
1547 is_last_selected = TRUE;
1548 while (is_last_selected) {
1550 gtk_tree_path_next (path);
1551 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1553 gtk_tree_model_get (priv->header_model, &tmp_iter,
1554 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1557 if (msg_is_visible (header, priv->is_outbox))
1558 is_last_selected = FALSE;
1559 g_object_unref(G_OBJECT(header));
1562 gtk_tree_path_free (path);
1563 return is_last_selected;
1567 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1569 ModestMsgViewWindowPrivate *priv;
1571 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1572 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1574 return priv->header_model != NULL;
1578 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1580 ModestMsgViewWindowPrivate *priv;
1582 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1583 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1585 return priv->is_search_result;
1589 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1591 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1593 if (!check_outbox) {
1596 ModestTnySendQueueStatus status;
1597 status = modest_tny_all_send_queues_get_msg_status (header);
1598 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1599 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1604 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1607 ModestMsgViewWindowPrivate *priv;
1608 gboolean is_first_selected;
1609 GtkTreeIter tmp_iter;
1611 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1612 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1614 /*if no model (so no rows at all), then virtually we are the first*/
1615 if (!priv->header_model || !priv->row_reference)
1618 if (!gtk_tree_row_reference_valid (priv->row_reference))
1621 path = gtk_tree_row_reference_get_path (priv->row_reference);
1625 is_first_selected = TRUE;
1626 while (is_first_selected) {
1628 if(!gtk_tree_path_prev (path))
1630 /* Here the 'if' is needless for logic, but let make sure
1631 * iter is valid for gtk_tree_model_get. */
1632 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1634 gtk_tree_model_get (priv->header_model, &tmp_iter,
1635 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1638 if (msg_is_visible (header, priv->is_outbox))
1639 is_first_selected = FALSE;
1640 g_object_unref(G_OBJECT(header));
1643 gtk_tree_path_free (path);
1644 return is_first_selected;
1649 GtkTreeRowReference *row_reference;
1653 message_reader_performer (gboolean canceled,
1655 GtkWindow *parent_window,
1656 TnyAccount *account,
1659 ModestMailOperation *mail_op = NULL;
1660 MsgReaderInfo *info;
1662 info = (MsgReaderInfo *) user_data;
1663 if (canceled || err) {
1667 /* Register the header - it'll be unregistered in the callback */
1668 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1670 /* New mail operation */
1671 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1672 modest_ui_actions_disk_operations_error_handler,
1675 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1676 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1677 g_object_unref (mail_op);
1679 /* Update dimming rules */
1680 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1681 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1684 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1685 g_object_unref (info->header);
1686 g_slice_free (MsgReaderInfo, info);
1691 * Reads the message whose summary item is @header. It takes care of
1692 * several things, among others:
1694 * If the message was not previously downloaded then ask the user
1695 * before downloading. If there is no connection launch the connection
1696 * dialog. Update toolbar dimming rules.
1698 * Returns: TRUE if the mail operation was started, otherwise if the
1699 * user do not want to download the message, or if the user do not
1700 * want to connect, then the operation is not issued
1703 message_reader (ModestMsgViewWindow *window,
1704 ModestMsgViewWindowPrivate *priv,
1706 GtkTreeRowReference *row_reference)
1708 ModestWindowMgr *mgr;
1709 TnyAccount *account;
1711 MsgReaderInfo *info;
1713 g_return_val_if_fail (row_reference != NULL, FALSE);
1715 mgr = modest_runtime_get_window_mgr ();
1716 /* Msg download completed */
1717 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1718 /* Ask the user if he wants to download the message if
1720 if (!tny_device_is_online (modest_runtime_get_device())) {
1721 GtkResponseType response;
1723 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1724 _("mcen_nc_get_msg"));
1725 if (response == GTK_RESPONSE_CANCEL)
1728 folder = tny_header_get_folder (header);
1729 info = g_slice_new (MsgReaderInfo);
1730 info->header = g_object_ref (header);
1731 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1733 /* Offer the connection dialog if necessary */
1734 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1736 TNY_FOLDER_STORE (folder),
1737 message_reader_performer,
1739 g_object_unref (folder);
1744 folder = tny_header_get_folder (header);
1745 account = tny_folder_get_account (folder);
1746 info = g_slice_new (MsgReaderInfo);
1747 info->header = g_object_ref (header);
1748 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1750 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1751 g_object_unref (account);
1752 g_object_unref (folder);
1758 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1760 ModestMsgViewWindowPrivate *priv;
1761 GtkTreePath *path= NULL;
1762 GtkTreeIter tmp_iter;
1764 gboolean retval = TRUE;
1765 GtkTreeRowReference *row_reference = NULL;
1767 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1768 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1770 if (!priv->row_reference)
1773 /* Update the next row reference if it's not valid. This could
1774 happen if for example the header which it was pointing to,
1775 was deleted. The best place to do it is in the row-deleted
1776 handler but the tinymail model do not work like the glib
1777 tree models and reports the deletion when the row is still
1779 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1780 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1781 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1782 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1785 if (priv->next_row_reference)
1786 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1790 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1792 gtk_tree_model_get_iter (priv->header_model,
1795 gtk_tree_path_free (path);
1797 gtk_tree_model_get (priv->header_model, &tmp_iter,
1798 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1801 /* Read the message & show it */
1802 if (!message_reader (window, priv, header, row_reference)) {
1805 gtk_tree_row_reference_free (row_reference);
1808 g_object_unref (header);
1814 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1816 ModestMsgViewWindowPrivate *priv = NULL;
1818 gboolean finished = FALSE;
1819 gboolean retval = FALSE;
1821 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1822 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1824 /* Return inmediatly if there is no header model */
1825 if (!priv->header_model || !priv->row_reference)
1828 path = gtk_tree_row_reference_get_path (priv->row_reference);
1829 while (!finished && gtk_tree_path_prev (path)) {
1833 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1834 gtk_tree_model_get (priv->header_model, &iter,
1835 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1839 if (msg_is_visible (header, priv->is_outbox)) {
1840 GtkTreeRowReference *row_reference;
1841 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1842 /* Read the message & show it */
1843 retval = message_reader (window, priv, header, row_reference);
1844 gtk_tree_row_reference_free (row_reference);
1848 g_object_unref (header);
1852 gtk_tree_path_free (path);
1857 view_msg_cb (ModestMailOperation *mail_op,
1864 ModestMsgViewWindow *self = NULL;
1865 ModestMsgViewWindowPrivate *priv = NULL;
1866 GtkTreeRowReference *row_reference = NULL;
1868 /* Unregister the header (it was registered before creating the mail operation) */
1869 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1871 row_reference = (GtkTreeRowReference *) user_data;
1873 gtk_tree_row_reference_free (row_reference);
1877 /* If there was any error */
1878 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1879 gtk_tree_row_reference_free (row_reference);
1883 /* Get the window */
1884 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1885 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1886 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1888 /* Update the row reference */
1889 if (priv->row_reference != NULL) {
1890 gtk_tree_row_reference_free (priv->row_reference);
1891 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1892 if (priv->next_row_reference != NULL) {
1893 gtk_tree_row_reference_free (priv->next_row_reference);
1895 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1896 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1899 /* Mark header as read */
1900 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1901 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1903 /* Set new message */
1904 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1905 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1906 modest_msg_view_window_update_priority (self);
1907 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1908 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1911 /* Set the new message uid of the window */
1912 if (priv->msg_uid) {
1913 g_free (priv->msg_uid);
1914 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1917 /* Notify the observers */
1918 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1919 0, priv->header_model, priv->row_reference);
1922 g_object_unref (self);
1923 gtk_tree_row_reference_free (row_reference);
1927 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1929 ModestMsgViewWindowPrivate *priv;
1931 TnyFolderType folder_type;
1933 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1935 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1937 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1941 folder = tny_msg_get_folder (msg);
1943 folder_type = modest_tny_folder_guess_folder_type (folder);
1944 g_object_unref (folder);
1946 g_object_unref (msg);
1954 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1956 ModestMsgViewWindowPrivate *priv;
1957 TnyHeader *header = NULL;
1958 TnyHeaderFlags flags = 0;
1960 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1962 if (priv->header_model && priv->row_reference) {
1964 GtkTreePath *path = NULL;
1966 path = gtk_tree_row_reference_get_path (priv->row_reference);
1967 g_return_if_fail (path != NULL);
1968 gtk_tree_model_get_iter (priv->header_model,
1970 gtk_tree_row_reference_get_path (priv->row_reference));
1972 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1974 gtk_tree_path_free (path);
1977 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1979 header = tny_msg_get_header (msg);
1980 g_object_unref (msg);
1985 flags = tny_header_get_flags (header);
1986 g_object_unref(G_OBJECT(header));
1989 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1994 toolbar_resize (ModestMsgViewWindow *self)
1996 ModestMsgViewWindowPrivate *priv = NULL;
1997 ModestWindowPrivate *parent_priv = NULL;
1999 gint static_button_size;
2000 ModestWindowMgr *mgr;
2002 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2003 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2004 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2006 mgr = modest_runtime_get_window_mgr ();
2007 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2009 if (parent_priv->toolbar) {
2010 /* left size buttons */
2011 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2012 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2013 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2014 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2015 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2016 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2017 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2018 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2019 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2020 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2021 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2022 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2023 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2024 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2025 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2026 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2028 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2029 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2030 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2031 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2032 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2033 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2034 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2035 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2041 modest_msg_view_window_show_toolbar (ModestWindow *self,
2042 gboolean show_toolbar)
2044 ModestMsgViewWindowPrivate *priv = NULL;
2045 ModestWindowPrivate *parent_priv;
2046 GtkWidget *reply_button = NULL, *menu = NULL;
2047 GtkWidget *placeholder = NULL;
2050 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2051 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2053 /* Set optimized view status */
2054 priv->optimized_view = !show_toolbar;
2056 if (!parent_priv->toolbar) {
2057 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2059 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2061 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2062 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2063 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2064 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2065 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2067 /* Add ProgressBar (Transfer toolbar) */
2068 priv->progress_bar = modest_progress_bar_new ();
2069 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2070 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2071 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2072 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2073 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2075 /* Connect cancel 'clicked' signal to abort progress mode */
2076 g_signal_connect(priv->cancel_toolitem, "clicked",
2077 G_CALLBACK(cancel_progressbar),
2080 /* Add it to the observers list */
2081 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2084 hildon_window_add_toolbar (HILDON_WINDOW (self),
2085 GTK_TOOLBAR (parent_priv->toolbar));
2087 /* Set reply button tap and hold menu */
2088 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2089 "/ToolBar/ToolbarMessageReply");
2090 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2091 "/ToolbarReplyCSM");
2092 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2096 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2097 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2098 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2100 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2101 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2102 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2104 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2107 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2108 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2113 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2115 ModestMsgViewWindow *window)
2117 if (!GTK_WIDGET_VISIBLE (window))
2120 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2124 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2126 ModestMsgViewWindowPrivate *priv;
2128 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2129 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2131 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2135 cancel_progressbar (GtkToolButton *toolbutton,
2136 ModestMsgViewWindow *self)
2139 ModestMsgViewWindowPrivate *priv;
2141 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2143 /* Get operation observers and cancel its current operation */
2144 tmp = priv->progress_widgets;
2146 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2147 tmp=g_slist_next(tmp);
2151 observers_empty (ModestMsgViewWindow *self)
2154 ModestMsgViewWindowPrivate *priv;
2155 gboolean is_empty = TRUE;
2156 guint pending_ops = 0;
2158 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2159 tmp = priv->progress_widgets;
2161 /* Check all observers */
2162 while (tmp && is_empty) {
2163 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2164 is_empty = pending_ops == 0;
2166 tmp = g_slist_next(tmp);
2173 on_account_removed (TnyAccountStore *account_store,
2174 TnyAccount *account,
2177 /* Do nothing if it's a transport account, because we only
2178 show the messages of a store account */
2179 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2180 const gchar *parent_acc = NULL;
2181 const gchar *our_acc = NULL;
2183 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2184 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2186 /* Close this window if I'm showing a message of the removed account */
2187 if (strcmp (parent_acc, our_acc) == 0)
2188 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2193 on_mail_operation_started (ModestMailOperation *mail_op,
2196 ModestMsgViewWindow *self;
2197 ModestMailOperationTypeOperation op_type;
2199 ModestMsgViewWindowPrivate *priv;
2200 GObject *source = NULL;
2202 self = MODEST_MSG_VIEW_WINDOW (user_data);
2203 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2204 op_type = modest_mail_operation_get_type_operation (mail_op);
2205 tmp = priv->progress_widgets;
2206 source = modest_mail_operation_get_source(mail_op);
2207 if (G_OBJECT (self) == source) {
2208 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2209 set_toolbar_transfer_mode(self);
2211 modest_progress_object_add_operation (
2212 MODEST_PROGRESS_OBJECT (tmp->data),
2214 tmp = g_slist_next (tmp);
2218 g_object_unref (source);
2222 on_mail_operation_finished (ModestMailOperation *mail_op,
2225 ModestMsgViewWindow *self;
2226 ModestMailOperationTypeOperation op_type;
2228 ModestMsgViewWindowPrivate *priv;
2230 self = MODEST_MSG_VIEW_WINDOW (user_data);
2231 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2232 op_type = modest_mail_operation_get_type_operation (mail_op);
2233 tmp = priv->progress_widgets;
2235 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2237 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2239 tmp = g_slist_next (tmp);
2242 /* If no more operations are being observed, NORMAL mode is enabled again */
2243 if (observers_empty (self)) {
2244 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2247 /* Update dimming rules. We have to do this right here
2248 and not in view_msg_cb because at that point the
2249 transfer mode is still enabled so the dimming rule
2250 won't let the user delete the message that has been
2251 readed for example */
2252 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2253 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2258 on_queue_changed (ModestMailOperationQueue *queue,
2259 ModestMailOperation *mail_op,
2260 ModestMailOperationQueueNotification type,
2261 ModestMsgViewWindow *self)
2263 ModestMsgViewWindowPrivate *priv;
2265 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2267 /* If this operations was created by another window, do nothing */
2268 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2271 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2272 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2274 "operation-started",
2275 G_CALLBACK (on_mail_operation_started),
2277 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2279 "operation-finished",
2280 G_CALLBACK (on_mail_operation_finished),
2282 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2283 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2285 "operation-started");
2286 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2288 "operation-finished");
2293 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2295 ModestMsgViewWindowPrivate *priv;
2296 TnyList *selected_attachments = NULL;
2298 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2299 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2301 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2303 return selected_attachments;
2309 guint banner_idle_id;
2310 } DecodeAsyncHelper;
2313 decode_async_banner_idle (gpointer user_data)
2315 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2317 helper->banner_idle_id = 0;
2318 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2319 g_object_ref (helper->banner);
2325 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2331 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2333 if (helper->banner_idle_id > 0) {
2334 g_source_remove (helper->banner_idle_id);
2335 helper->banner_idle_id = 0;
2337 if (helper->banner) {
2338 gtk_widget_destroy (helper->banner);
2340 if (cancelled || err) {
2341 modest_platform_information_banner (NULL, NULL,
2342 _("mail_ib_file_operation_failed"));
2346 /* make the file read-only */
2347 g_chmod(helper->filepath, 0444);
2349 /* Activate the file */
2350 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2354 g_free (helper->filepath);
2355 g_object_unref (helper->banner);
2356 g_slice_free (DecodeAsyncHelper, helper);
2360 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2361 TnyMimePart *mime_part)
2363 ModestMsgViewWindowPrivate *priv;
2364 const gchar *msg_uid;
2365 gchar *attachment_uid = NULL;
2366 gint attachment_index = 0;
2367 TnyList *attachments;
2369 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2370 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2371 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2373 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2374 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2375 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2376 g_object_unref (attachments);
2378 if (msg_uid && attachment_index >= 0) {
2379 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2382 if (mime_part == NULL) {
2383 gboolean error = FALSE;
2384 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2385 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2387 } else if (tny_list_get_length (selected_attachments) > 1) {
2388 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2392 iter = tny_list_create_iterator (selected_attachments);
2393 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2394 g_object_unref (iter);
2396 g_object_unref (selected_attachments);
2401 g_object_ref (mime_part);
2404 if (tny_mime_part_is_purged (mime_part)) {
2405 g_object_unref (mime_part);
2409 if (!modest_tny_mime_part_is_msg (mime_part)) {
2410 gchar *filepath = NULL;
2411 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2412 gboolean show_error_banner = FALSE;
2413 TnyFsStream *temp_stream = NULL;
2414 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2417 if (temp_stream != NULL) {
2418 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2419 helper->filepath = g_strdup (filepath);
2420 helper->banner = NULL;
2421 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2422 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2423 on_decode_to_stream_async_handler,
2426 g_object_unref (temp_stream);
2427 /* NOTE: files in the temporary area will be automatically
2428 * cleaned after some time if they are no longer in use */
2431 const gchar *content_type;
2432 /* the file may already exist but it isn't writable,
2433 * let's try to open it anyway */
2434 content_type = tny_mime_part_get_content_type (mime_part);
2435 modest_platform_activate_file (filepath, content_type);
2437 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2438 show_error_banner = TRUE;
2443 if (show_error_banner)
2444 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2446 /* message attachment */
2447 TnyHeader *header = NULL;
2448 ModestWindowMgr *mgr;
2449 ModestWindow *msg_win = NULL;
2452 header = tny_msg_get_header (TNY_MSG (mime_part));
2453 mgr = modest_runtime_get_window_mgr ();
2454 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2457 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2458 * thus, we don't do anything */
2459 g_warning ("window for is already being created");
2461 /* it's not found, so create a new window for it */
2462 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2463 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2465 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2466 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2467 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2468 modest_window_get_zoom (MODEST_WINDOW (window)));
2469 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2470 gtk_widget_show_all (GTK_WIDGET (msg_win));
2473 g_object_unref (mime_part);
2486 GnomeVFSResult result;
2489 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2490 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2491 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2492 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2495 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2499 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2500 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2501 g_free (pair->filename);
2502 g_object_unref (pair->part);
2503 g_slice_free (SaveMimePartPair, pair);
2505 g_list_free (info->pairs);
2508 gtk_widget_destroy (info->banner);
2509 g_slice_free (SaveMimePartInfo, info);
2514 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2516 if (info->pairs != NULL) {
2517 save_mime_part_to_file (info);
2519 /* This is a GDK lock because we are an idle callback and
2520 * hildon_banner_show_information is or does Gtk+ code */
2522 gdk_threads_enter (); /* CHECKED */
2523 save_mime_part_info_free (info, TRUE);
2524 if (info->result == GNOME_VFS_OK) {
2525 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2526 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2527 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2528 "cerm_device_memory_full"));
2530 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2532 gdk_threads_leave (); /* CHECKED */
2539 save_mime_part_to_file (SaveMimePartInfo *info)
2541 GnomeVFSHandle *handle;
2543 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2545 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2546 if (info->result == GNOME_VFS_OK) {
2547 GError *error = NULL;
2548 stream = tny_vfs_stream_new (handle);
2549 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2550 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2552 info->result = GNOME_VFS_ERROR_IO;
2554 g_object_unref (G_OBJECT (stream));
2555 g_object_unref (pair->part);
2556 g_slice_free (SaveMimePartPair, pair);
2557 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2559 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2560 save_mime_part_info_free (info, FALSE);
2563 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2568 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2570 gboolean is_ok = TRUE;
2571 gint replaced_files = 0;
2572 const GList *files = info->pairs;
2575 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2576 SaveMimePartPair *pair = iter->data;
2577 if (modest_utils_file_exists (pair->filename)) {
2581 if (replaced_files) {
2582 GtkWidget *confirm_overwrite_dialog;
2583 const gchar *message = (replaced_files == 1) ?
2584 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2585 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2586 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2589 gtk_widget_destroy (confirm_overwrite_dialog);
2593 save_mime_part_info_free (info, TRUE);
2595 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2596 _CS("sfil_ib_saving"));
2597 info->banner = banner;
2598 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2604 save_attachments_response (GtkDialog *dialog,
2608 TnyList *mime_parts;
2610 GList *files_to_save = NULL;
2612 mime_parts = TNY_LIST (user_data);
2614 if (arg1 != GTK_RESPONSE_OK)
2617 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2619 if (!modest_utils_folder_writable (chooser_uri)) {
2620 hildon_banner_show_information
2621 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2625 iter = tny_list_create_iterator (mime_parts);
2626 while (!tny_iterator_is_done (iter)) {
2627 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2629 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2630 !tny_mime_part_is_purged (mime_part) &&
2631 (tny_mime_part_get_filename (mime_part) != NULL)) {
2632 SaveMimePartPair *pair;
2634 pair = g_slice_new0 (SaveMimePartPair);
2636 if (tny_list_get_length (mime_parts) > 1) {
2638 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2639 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2642 pair->filename = g_strdup (chooser_uri);
2644 pair->part = mime_part;
2645 files_to_save = g_list_prepend (files_to_save, pair);
2647 tny_iterator_next (iter);
2649 g_object_unref (iter);
2651 g_free (chooser_uri);
2653 if (files_to_save != NULL) {
2654 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2655 info->pairs = files_to_save;
2656 info->result = TRUE;
2657 save_mime_parts_to_file_with_checks (info);
2661 /* Free and close the dialog */
2662 g_object_unref (mime_parts);
2663 gtk_widget_destroy (GTK_WIDGET (dialog));
2667 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2669 ModestMsgViewWindowPrivate *priv;
2670 GtkWidget *save_dialog = NULL;
2671 gchar *folder = NULL;
2672 gchar *filename = NULL;
2673 gchar *save_multiple_str = NULL;
2675 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2676 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2678 if (mime_parts == NULL) {
2679 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2680 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2683 g_object_ref (mime_parts);
2686 /* prepare dialog */
2687 if (tny_list_get_length (mime_parts) == 1) {
2689 /* only one attachment selected */
2690 iter = tny_list_create_iterator (mime_parts);
2691 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2692 g_object_unref (iter);
2693 if (!modest_tny_mime_part_is_msg (mime_part) &&
2694 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2695 !tny_mime_part_is_purged (mime_part)) {
2696 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2698 /* TODO: show any error? */
2699 g_warning ("Tried to save a non-file attachment");
2700 g_object_unref (mime_parts);
2703 g_object_unref (mime_part);
2705 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2706 tny_list_get_length (mime_parts));
2709 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2710 GTK_FILE_CHOOSER_ACTION_SAVE);
2713 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2714 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2719 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2724 /* if multiple, set multiple string */
2725 if (save_multiple_str) {
2726 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2727 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2730 /* We must run this asynchronously, because the hildon dialog
2731 performs a gtk_dialog_run by itself which leads to gdk
2733 g_signal_connect (save_dialog, "response",
2734 G_CALLBACK (save_attachments_response), mime_parts);
2736 gtk_widget_show_all (save_dialog);
2740 show_remove_attachment_information (gpointer userdata)
2742 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2743 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2745 /* We're outside the main lock */
2746 gdk_threads_enter ();
2748 if (priv->remove_attachment_banner != NULL) {
2749 gtk_widget_destroy (priv->remove_attachment_banner);
2750 g_object_unref (priv->remove_attachment_banner);
2753 priv->remove_attachment_banner = g_object_ref (
2754 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2756 gdk_threads_leave ();
2762 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2764 ModestMsgViewWindowPrivate *priv;
2765 TnyList *mime_parts = NULL;
2766 gchar *confirmation_message;
2772 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2773 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2776 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2778 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2780 /* Remove already purged messages from mime parts list */
2781 iter = tny_list_create_iterator (mime_parts);
2782 while (!tny_iterator_is_done (iter)) {
2783 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2784 tny_iterator_next (iter);
2785 if (tny_mime_part_is_purged (part)) {
2786 tny_list_remove (mime_parts, (GObject *) part);
2788 g_object_unref (part);
2790 g_object_unref (iter);
2792 if (tny_list_get_length (mime_parts) == 0) {
2793 g_object_unref (mime_parts);
2797 n_attachments = tny_list_get_length (mime_parts);
2798 if (n_attachments == 1) {
2802 iter = tny_list_create_iterator (mime_parts);
2803 part = (TnyMimePart *) tny_iterator_get_current (iter);
2804 g_object_unref (iter);
2805 if (modest_tny_mime_part_is_msg (part)) {
2807 header = tny_msg_get_header (TNY_MSG (part));
2808 filename = tny_header_dup_subject (header);
2809 g_object_unref (header);
2810 if (filename == NULL)
2811 filename = g_strdup (_("mail_va_no_subject"));
2813 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2815 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2817 g_object_unref (part);
2819 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2820 "mcen_nc_purge_files_text",
2821 n_attachments), n_attachments);
2823 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2824 confirmation_message);
2825 g_free (confirmation_message);
2827 if (response != GTK_RESPONSE_OK) {
2828 g_object_unref (mime_parts);
2832 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2834 iter = tny_list_create_iterator (mime_parts);
2835 while (!tny_iterator_is_done (iter)) {
2838 part = (TnyMimePart *) tny_iterator_get_current (iter);
2839 tny_mime_part_set_purged (TNY_MIME_PART (part));
2840 g_object_unref (part);
2841 tny_iterator_next (iter);
2843 g_object_unref (iter);
2845 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2846 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2847 tny_msg_rewrite_cache (msg);
2848 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2849 g_object_unref (msg);
2851 g_object_unref (mime_parts);
2853 if (priv->purge_timeout > 0) {
2854 g_source_remove (priv->purge_timeout);
2855 priv->purge_timeout = 0;
2858 if (priv->remove_attachment_banner) {
2859 gtk_widget_destroy (priv->remove_attachment_banner);
2860 g_object_unref (priv->remove_attachment_banner);
2861 priv->remove_attachment_banner = NULL;
2869 update_window_title (ModestMsgViewWindow *window)
2871 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2873 TnyHeader *header = NULL;
2874 gchar *subject = NULL;
2876 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2879 header = tny_msg_get_header (msg);
2880 subject = tny_header_dup_subject (header);
2881 g_object_unref (header);
2882 g_object_unref (msg);
2885 if ((subject == NULL)||(subject[0] == '\0')) {
2887 subject = g_strdup (_("mail_va_no_subject"));
2890 gtk_window_set_title (GTK_WINDOW (window), subject);
2894 static void on_move_focus (GtkWidget *widget,
2895 GtkDirectionType direction,
2898 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
2902 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
2904 GnomeVFSResult result;
2905 GnomeVFSHandle *handle = NULL;
2906 GnomeVFSFileInfo *info = NULL;
2909 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2910 if (result != GNOME_VFS_OK) {
2915 info = gnome_vfs_file_info_new ();
2916 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
2917 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
2918 /* We put a "safe" default size for going to cache */
2919 *expected_size = (300*1024);
2921 *expected_size = info->size;
2923 gnome_vfs_file_info_unref (info);
2925 stream = tny_vfs_stream_new (handle);
2934 TnyStream *output_stream;
2935 GtkWidget *msg_view;
2939 on_fetch_image_idle_refresh_view (gpointer userdata)
2942 FetchImageData *fidata = (FetchImageData *) userdata;
2943 g_message ("REFRESH VIEW");
2944 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
2945 g_message ("QUEUING DRAW");
2946 gtk_widget_queue_draw (fidata->msg_view);
2948 g_object_unref (fidata->msg_view);
2949 g_slice_free (FetchImageData, fidata);
2954 on_fetch_image_thread (gpointer userdata)
2956 FetchImageData *fidata = (FetchImageData *) userdata;
2957 TnyStreamCache *cache;
2958 TnyStream *cache_stream;
2960 cache = modest_runtime_get_images_cache ();
2961 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
2962 g_free (fidata->cache_id);
2963 g_free (fidata->uri);
2965 if (cache_stream != NULL) {
2966 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
2967 tny_stream_close (cache_stream);
2968 g_object_unref (cache_stream);
2971 tny_stream_close (fidata->output_stream);
2972 g_object_unref (fidata->output_stream);
2975 gdk_threads_enter ();
2976 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
2977 gdk_threads_leave ();
2983 on_fetch_image (ModestMsgView *msgview,
2986 ModestMsgViewWindow *window)
2988 const gchar *current_account;
2989 ModestMsgViewWindowPrivate *priv;
2990 FetchImageData *fidata;
2992 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2994 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
2996 fidata = g_slice_new0 (FetchImageData);
2997 fidata->msg_view = g_object_ref (msgview);
2998 fidata->uri = g_strdup (uri);
2999 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3000 fidata->output_stream = g_object_ref (stream);
3002 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3003 g_object_unref (fidata->output_stream);
3004 g_free (fidata->cache_id);
3005 g_free (fidata->uri);
3006 g_object_unref (fidata->msg_view);
3007 g_slice_free (FetchImageData, fidata);
3008 tny_stream_close (stream);