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 <hildon/hildon-picker-dialog.h>
53 #include "modest-defs.h"
54 #include "modest-hildon-includes.h"
55 #include "modest-ui-dimming-manager.h"
56 #include <gdk/gdkkeysyms.h>
57 #include <modest-tny-account.h>
58 #include <modest-mime-part-view.h>
59 #include <modest-isearch-view.h>
60 #include <modest-tny-mime-part.h>
61 #include <modest-address-book.h>
64 #include <glib/gstdio.h>
65 #include <modest-debug.h>
67 #define MYDOCS_ENV "MYDOCSDIR"
68 #define DOCS_FOLDER ".documents"
70 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
71 struct _ModestMsgViewWindowPrivate {
74 GtkWidget *main_scroll;
75 GtkWidget *find_toolbar;
78 /* Progress observers */
79 GtkWidget *progress_bar;
80 GSList *progress_widgets;
83 GtkWidget *progress_toolitem;
84 GtkWidget *cancel_toolitem;
85 GtkWidget *prev_toolitem;
86 GtkWidget *next_toolitem;
87 ModestToolBarModes current_toolbar_mode;
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;
118 guint progress_bar_timeout;
125 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
126 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
127 static void modest_header_view_observer_init(
128 ModestHeaderViewObserverIface *iface_class);
129 static void modest_msg_view_window_finalize (GObject *obj);
130 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
132 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
133 ModestMsgViewWindow *obj);
134 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
135 ModestMsgViewWindow *obj);
137 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
139 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
140 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
144 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
146 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
147 gboolean show_toolbar);
149 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
151 ModestMsgViewWindow *window);
153 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
156 ModestMsgViewWindow *window);
158 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
160 ModestMsgViewWindow *window);
162 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
163 GtkTreePath *tree_path,
164 GtkTreeIter *tree_iter,
165 ModestMsgViewWindow *window);
167 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
171 ModestMsgViewWindow *window);
173 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
175 const gchar *tny_folder_id);
177 static void cancel_progressbar (GtkToolButton *toolbutton,
178 ModestMsgViewWindow *self);
180 static void on_queue_changed (ModestMailOperationQueue *queue,
181 ModestMailOperation *mail_op,
182 ModestMailOperationQueueNotification type,
183 ModestMsgViewWindow *self);
185 static void on_account_removed (TnyAccountStore *account_store,
189 static void on_move_focus (GtkWidget *widget,
190 GtkDirectionType direction,
193 static void view_msg_cb (ModestMailOperation *mail_op,
200 static void set_toolbar_mode (ModestMsgViewWindow *self,
201 ModestToolBarModes mode);
203 static void update_window_title (ModestMsgViewWindow *window);
205 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
206 static void init_window (ModestMsgViewWindow *obj);
208 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
210 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
212 static gboolean on_fetch_image (ModestMsgView *msgview,
215 ModestMsgViewWindow *window);
217 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
218 GtkScrollType scroll_type,
221 static gboolean message_reader (ModestMsgViewWindow *window,
222 ModestMsgViewWindowPrivate *priv,
224 GtkTreeRowReference *row_reference);
226 /* list my signals */
233 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
234 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
237 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
238 MODEST_TYPE_MSG_VIEW_WINDOW, \
239 ModestMsgViewWindowPrivate))
241 static GtkWindowClass *parent_class = NULL;
243 /* uncomment the following if you have defined any signals */
244 static guint signals[LAST_SIGNAL] = {0};
247 modest_msg_view_window_get_type (void)
249 static GType my_type = 0;
251 static const GTypeInfo my_info = {
252 sizeof(ModestMsgViewWindowClass),
253 NULL, /* base init */
254 NULL, /* base finalize */
255 (GClassInitFunc) modest_msg_view_window_class_init,
256 NULL, /* class finalize */
257 NULL, /* class data */
258 sizeof(ModestMsgViewWindow),
260 (GInstanceInitFunc) modest_msg_view_window_init,
263 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
264 "ModestMsgViewWindow",
267 static const GInterfaceInfo modest_header_view_observer_info =
269 (GInterfaceInitFunc) modest_header_view_observer_init,
270 NULL, /* interface_finalize */
271 NULL /* interface_data */
274 g_type_add_interface_static (my_type,
275 MODEST_TYPE_HEADER_VIEW_OBSERVER,
276 &modest_header_view_observer_info);
282 save_state (ModestWindow *self)
284 modest_widget_memory_save (modest_runtime_get_conf (),
286 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
290 restore_settings (ModestMsgViewWindow *self)
294 conf = modest_runtime_get_conf ();
295 modest_widget_memory_restore (conf,
297 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
300 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
301 GtkScrollType scroll_type,
305 ModestMsgViewWindowPrivate *priv;
306 gboolean return_value;
308 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
309 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
314 add_scroll_binding (GtkBindingSet *binding_set,
316 GtkScrollType scroll)
318 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
320 gtk_binding_entry_add_signal (binding_set, keyval, 0,
322 GTK_TYPE_SCROLL_TYPE, scroll,
323 G_TYPE_BOOLEAN, FALSE);
324 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
326 GTK_TYPE_SCROLL_TYPE, scroll,
327 G_TYPE_BOOLEAN, FALSE);
331 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
333 GObjectClass *gobject_class;
334 ModestWindowClass *modest_window_class;
335 GtkBindingSet *binding_set;
337 gobject_class = (GObjectClass*) klass;
338 modest_window_class = (ModestWindowClass *) klass;
340 parent_class = g_type_class_peek_parent (klass);
341 gobject_class->finalize = modest_msg_view_window_finalize;
343 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
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->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
445 priv->optimized_view = FALSE;
446 priv->progress_bar_timeout = 0;
447 priv->purge_timeout = 0;
448 priv->remove_attachment_banner = NULL;
449 priv->msg_uid = NULL;
451 priv->sighandlers = NULL;
454 init_window (MODEST_MSG_VIEW_WINDOW(obj));
456 /* Set window icon */
457 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
459 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
460 g_object_unref (window_icon);
463 hildon_program_add_window (hildon_program_get_instance(),
470 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
472 ModestMsgViewWindowPrivate *priv = NULL;
474 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
476 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
478 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
480 if (priv->progress_bar_timeout > 0) {
481 g_source_remove (priv->progress_bar_timeout);
482 priv->progress_bar_timeout = 0;
489 set_toolbar_mode (ModestMsgViewWindow *self,
490 ModestToolBarModes mode)
492 ModestWindowPrivate *parent_priv;
493 ModestMsgViewWindowPrivate *priv;
494 /* GtkWidget *widget = NULL; */
496 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
498 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
499 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
501 /* Sets current toolbar mode */
502 priv->current_toolbar_mode = mode;
504 /* Update toolbar dimming state */
505 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
508 case TOOLBAR_MODE_NORMAL:
509 if (priv->progress_toolitem) {
510 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
511 gtk_widget_hide (priv->progress_toolitem);
514 if (priv->progress_bar)
515 gtk_widget_hide (priv->progress_bar);
517 if (priv->cancel_toolitem)
518 gtk_widget_hide (priv->cancel_toolitem);
520 if (priv->prev_toolitem)
521 gtk_widget_show (priv->prev_toolitem);
523 if (priv->next_toolitem)
524 gtk_widget_show (priv->next_toolitem);
526 /* Hide toolbar if optimized view is enabled */
527 if (priv->optimized_view) {
528 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
529 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
533 case TOOLBAR_MODE_TRANSFER:
534 if (priv->prev_toolitem)
535 gtk_widget_hide (priv->prev_toolitem);
537 if (priv->next_toolitem)
538 gtk_widget_hide (priv->next_toolitem);
540 if (priv->progress_bar)
541 gtk_widget_show (priv->progress_bar);
543 if (priv->progress_toolitem) {
544 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
545 gtk_widget_show (priv->progress_toolitem);
548 if (priv->cancel_toolitem)
549 gtk_widget_show (priv->cancel_toolitem);
551 /* Show toolbar if it's hiden (optimized view ) */
552 if (priv->optimized_view) {
553 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
554 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
559 g_return_if_reached ();
566 init_window (ModestMsgViewWindow *obj)
568 GtkWidget *main_vbox;
569 ModestMsgViewWindowPrivate *priv;
571 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
573 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
574 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
575 main_vbox = gtk_vbox_new (FALSE, 6);
576 #ifdef MODEST_TOOLKIT_HILDON2
577 priv->main_scroll = hildon_pannable_area_new ();
578 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
580 #ifdef MODEST_USE_MOZEMBED
581 priv->main_scroll = priv->msg_view;
582 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
584 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
585 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
587 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
588 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
589 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
592 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
593 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
595 priv->find_toolbar = hildon_find_toolbar_new (NULL);
596 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
597 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
599 gtk_widget_show_all (GTK_WIDGET(main_vbox));
603 modest_msg_view_window_disconnect_signals (ModestWindow *self)
605 ModestMsgViewWindowPrivate *priv;
606 ModestHeaderView *header_view = NULL;
607 ModestWindow *main_window = NULL;
609 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
611 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
612 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
613 priv->clipboard_change_handler))
614 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
615 priv->clipboard_change_handler);
617 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
618 priv->queue_change_handler))
619 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
620 priv->queue_change_handler);
622 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
623 priv->account_removed_handler))
624 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
625 priv->account_removed_handler);
627 if (priv->header_model) {
628 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
629 priv->row_changed_handler))
630 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
631 priv->row_changed_handler);
633 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
634 priv->row_deleted_handler))
635 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
636 priv->row_deleted_handler);
638 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
639 priv->row_inserted_handler))
640 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
641 priv->row_inserted_handler);
643 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
644 priv->rows_reordered_handler))
645 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
646 priv->rows_reordered_handler);
649 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
650 priv->sighandlers = NULL;
652 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
653 FALSE); /* don't create */
657 header_view = MODEST_HEADER_VIEW(
658 modest_main_window_get_child_widget(
659 MODEST_MAIN_WINDOW(main_window),
660 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
661 if (header_view == NULL)
664 modest_header_view_remove_observer(header_view,
665 MODEST_HEADER_VIEW_OBSERVER(self));
669 modest_msg_view_window_finalize (GObject *obj)
671 ModestMsgViewWindowPrivate *priv;
673 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
675 /* Sanity check: shouldn't be needed, the window mgr should
676 call this function before */
677 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
679 if (priv->header_model != NULL) {
680 g_object_unref (priv->header_model);
681 priv->header_model = NULL;
684 if (priv->progress_bar_timeout > 0) {
685 g_source_remove (priv->progress_bar_timeout);
686 priv->progress_bar_timeout = 0;
689 if (priv->remove_attachment_banner) {
690 gtk_widget_destroy (priv->remove_attachment_banner);
691 g_object_unref (priv->remove_attachment_banner);
692 priv->remove_attachment_banner = NULL;
695 if (priv->purge_timeout > 0) {
696 g_source_remove (priv->purge_timeout);
697 priv->purge_timeout = 0;
700 if (priv->row_reference) {
701 gtk_tree_row_reference_free (priv->row_reference);
702 priv->row_reference = NULL;
705 if (priv->next_row_reference) {
706 gtk_tree_row_reference_free (priv->next_row_reference);
707 priv->next_row_reference = NULL;
711 g_free (priv->msg_uid);
712 priv->msg_uid = NULL;
715 G_OBJECT_CLASS(parent_class)->finalize (obj);
719 select_next_valid_row (GtkTreeModel *model,
720 GtkTreeRowReference **row_reference,
724 GtkTreeIter tmp_iter;
726 GtkTreePath *next = NULL;
727 gboolean retval = FALSE, finished;
729 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
731 path = gtk_tree_row_reference_get_path (*row_reference);
732 gtk_tree_model_get_iter (model, &tmp_iter, path);
733 gtk_tree_row_reference_free (*row_reference);
734 *row_reference = NULL;
738 TnyHeader *header = NULL;
740 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
741 gtk_tree_model_get (model, &tmp_iter,
742 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
746 if (msg_is_visible (header, is_outbox)) {
747 next = gtk_tree_model_get_path (model, &tmp_iter);
748 *row_reference = gtk_tree_row_reference_new (model, next);
749 gtk_tree_path_free (next);
753 g_object_unref (header);
756 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
757 next = gtk_tree_model_get_path (model, &tmp_iter);
759 /* Ensure that we are not selecting the same */
760 if (gtk_tree_path_compare (path, next) != 0) {
761 gtk_tree_model_get (model, &tmp_iter,
762 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
765 if (msg_is_visible (header, is_outbox)) {
766 *row_reference = gtk_tree_row_reference_new (model, next);
770 g_object_unref (header);
774 /* If we ended up in the same message
775 then there is no valid next
779 gtk_tree_path_free (next);
781 /* If there are no more messages and we don't
782 want to start again in the first one then
783 there is no valid next message */
789 gtk_tree_path_free (path);
794 /* TODO: This should be in _init(), with the parameters as properties. */
796 modest_msg_view_window_construct (ModestMsgViewWindow *self,
797 const gchar *modest_account_name,
798 const gchar *msg_uid)
801 ModestMsgViewWindowPrivate *priv = NULL;
802 ModestWindowPrivate *parent_priv = NULL;
803 ModestDimmingRulesGroup *menu_rules_group = NULL;
804 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
805 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
807 obj = G_OBJECT (self);
808 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
809 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
811 priv->msg_uid = g_strdup (msg_uid);
814 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
815 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
816 gtk_widget_show (parent_priv->menubar);
817 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
819 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
820 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
821 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
823 /* Add common dimming rules */
824 modest_dimming_rules_group_add_rules (menu_rules_group,
825 modest_msg_view_menu_dimming_entries,
826 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
827 MODEST_WINDOW (self));
828 modest_dimming_rules_group_add_rules (toolbar_rules_group,
829 modest_msg_view_toolbar_dimming_entries,
830 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
831 MODEST_WINDOW (self));
832 modest_dimming_rules_group_add_rules (clipboard_rules_group,
833 modest_msg_view_clipboard_dimming_entries,
834 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
835 MODEST_WINDOW (self));
837 /* Insert dimming rules group for this window */
838 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
839 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
840 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
841 g_object_unref (menu_rules_group);
842 g_object_unref (toolbar_rules_group);
843 g_object_unref (clipboard_rules_group);
845 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
847 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);
848 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
849 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
850 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
851 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
852 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
853 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
854 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
855 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
856 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
857 G_CALLBACK (modest_ui_actions_on_details), obj);
858 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
859 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
860 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
861 G_CALLBACK (on_fetch_image), obj);
863 g_signal_connect (G_OBJECT (obj), "key-release-event",
864 G_CALLBACK (modest_msg_view_window_key_event),
867 g_signal_connect (G_OBJECT (obj), "key-press-event",
868 G_CALLBACK (modest_msg_view_window_key_event),
871 g_signal_connect (G_OBJECT (obj), "move-focus",
872 G_CALLBACK (on_move_focus), obj);
874 /* Mail Operation Queue */
875 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
877 G_CALLBACK (on_queue_changed),
880 /* Account manager */
881 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
883 G_CALLBACK(on_account_removed),
886 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
888 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
889 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
890 priv->last_search = NULL;
892 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
894 /* Init the clipboard actions dim status */
895 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
897 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
902 /* FIXME: parameter checks */
904 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
905 const gchar *modest_account_name,
906 const gchar *msg_uid,
908 GtkTreeRowReference *row_reference)
910 ModestMsgViewWindow *window = NULL;
911 ModestMsgViewWindowPrivate *priv = NULL;
912 TnyFolder *header_folder = NULL;
913 ModestHeaderView *header_view = NULL;
914 ModestWindow *main_window = NULL;
915 ModestWindowMgr *mgr = NULL;
918 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
921 mgr = modest_runtime_get_window_mgr ();
922 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
923 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
925 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
927 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
929 /* Remember the message list's TreeModel so we can detect changes
930 * and change the list selection when necessary: */
932 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
934 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
935 MODEST_MAIN_WINDOW(main_window),
936 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
939 if (header_view != NULL){
940 header_folder = modest_header_view_get_folder(header_view);
941 /* This could happen if the header folder was
942 unseleted before opening this msg window (for
943 example if the user selects an account in the
944 folder view of the main window */
946 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
947 priv->header_folder_id = tny_folder_get_id(header_folder);
948 g_assert(priv->header_folder_id != NULL);
949 g_object_unref(header_folder);
953 /* Setup row references and connect signals */
954 priv->header_model = g_object_ref (model);
957 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
958 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
959 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
961 priv->row_reference = NULL;
962 priv->next_row_reference = NULL;
965 /* Connect signals */
966 priv->row_changed_handler =
967 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
968 G_CALLBACK(modest_msg_view_window_on_row_changed),
970 priv->row_deleted_handler =
971 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
972 G_CALLBACK(modest_msg_view_window_on_row_deleted),
974 priv->row_inserted_handler =
975 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
976 G_CALLBACK(modest_msg_view_window_on_row_inserted),
978 priv->rows_reordered_handler =
979 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
980 G_CALLBACK(modest_msg_view_window_on_row_reordered),
983 if (header_view != NULL){
984 modest_header_view_add_observer(header_view,
985 MODEST_HEADER_VIEW_OBSERVER(window));
988 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
989 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
991 /* gtk_widget_show_all (GTK_WIDGET (window)); */
992 modest_msg_view_window_update_priority (window);
993 /* Check dimming rules */
994 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
995 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
996 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
998 return MODEST_WINDOW(window);
1002 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
1003 const gchar *modest_account_name,
1004 const gchar *msg_uid,
1005 GtkTreeRowReference *row_reference)
1007 ModestMsgViewWindow *window = NULL;
1008 ModestMsgViewWindowPrivate *priv = NULL;
1009 TnyFolder *header_folder = NULL;
1010 ModestWindowMgr *mgr = NULL;
1014 mgr = modest_runtime_get_window_mgr ();
1015 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1016 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1018 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1020 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1022 /* Remember the message list's TreeModel so we can detect changes
1023 * and change the list selection when necessary: */
1025 if (header_view != NULL){
1026 header_folder = modest_header_view_get_folder(header_view);
1027 /* This could happen if the header folder was
1028 unseleted before opening this msg window (for
1029 example if the user selects an account in the
1030 folder view of the main window */
1031 if (header_folder) {
1032 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
1033 priv->header_folder_id = tny_folder_get_id(header_folder);
1034 g_assert(priv->header_folder_id != NULL);
1035 g_object_unref(header_folder);
1039 /* Setup row references and connect signals */
1040 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1042 if (row_reference) {
1043 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1044 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
1045 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1047 priv->row_reference = NULL;
1048 priv->next_row_reference = NULL;
1051 /* Connect signals */
1052 priv->row_changed_handler =
1053 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
1054 G_CALLBACK(modest_msg_view_window_on_row_changed),
1056 priv->row_deleted_handler =
1057 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
1058 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1060 priv->row_inserted_handler =
1061 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
1062 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1064 priv->rows_reordered_handler =
1065 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1066 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1069 if (header_view != NULL){
1070 modest_header_view_add_observer(header_view,
1071 MODEST_HEADER_VIEW_OBSERVER(window));
1074 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1076 path = gtk_tree_row_reference_get_path (row_reference);
1077 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1079 gtk_tree_model_get (priv->header_model, &iter,
1080 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1082 message_reader (window, priv, header, row_reference);
1084 gtk_tree_path_free (path);
1086 /* Check dimming rules */
1087 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1088 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1089 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1091 return MODEST_WINDOW(window);
1095 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1096 const gchar *modest_account_name,
1097 const gchar *msg_uid)
1099 ModestMsgViewWindow *window = NULL;
1100 ModestMsgViewWindowPrivate *priv = NULL;
1101 ModestWindowMgr *mgr = NULL;
1103 mgr = modest_runtime_get_window_mgr ();
1104 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1105 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1106 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1108 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1110 /* Remember that this is a search result,
1111 * so we can disable some UI appropriately: */
1112 priv->is_search_result = TRUE;
1114 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1116 update_window_title (window);
1117 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1118 modest_msg_view_window_update_priority (window);
1120 /* Check dimming rules */
1121 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1122 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1123 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1125 return MODEST_WINDOW(window);
1129 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1130 const gchar *modest_account_name,
1131 const gchar *msg_uid)
1133 GObject *obj = NULL;
1134 ModestMsgViewWindowPrivate *priv;
1135 ModestWindowMgr *mgr = NULL;
1137 g_return_val_if_fail (msg, NULL);
1138 mgr = modest_runtime_get_window_mgr ();
1139 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1140 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1141 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1142 modest_account_name, msg_uid);
1144 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1145 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1147 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1149 /* Check dimming rules */
1150 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1151 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1152 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1154 return MODEST_WINDOW(obj);
1158 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1161 ModestMsgViewWindow *window)
1163 check_dimming_rules_after_change (window);
1167 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1169 ModestMsgViewWindow *window)
1171 check_dimming_rules_after_change (window);
1173 /* The window could have dissapeared */
1176 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1178 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1179 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1183 /* On insertions we check if the folder still has the message we are
1184 * showing or do not. If do not, we do nothing. Which means we are still
1185 * not attached to any header folder and thus next/prev buttons are
1186 * still dimmed. Once the message that is shown by msg-view is found, the
1187 * new model of header-view will be attached and the references will be set.
1188 * On each further insertions dimming rules will be checked. However
1189 * this requires extra CPU time at least works.
1190 * (An message might be deleted from TnyFolder and thus will not be
1191 * inserted into the model again for example if it is removed by the
1192 * imap server and the header view is refreshed.)
1195 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1196 GtkTreePath *tree_path,
1197 GtkTreeIter *tree_iter,
1198 ModestMsgViewWindow *window)
1200 ModestMsgViewWindowPrivate *priv = NULL;
1201 TnyHeader *header = NULL;
1203 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1204 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1206 g_assert (model == priv->header_model);
1208 /* Check if the newly inserted message is the same we are actually
1209 * showing. IF not, we should remain detached from the header model
1210 * and thus prev and next toolbar buttons should remain dimmed. */
1211 gtk_tree_model_get (model, tree_iter,
1212 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1215 if (TNY_IS_HEADER (header)) {
1218 uid = modest_tny_folder_get_header_unique_id (header);
1219 if (!g_str_equal(priv->msg_uid, uid)) {
1220 check_dimming_rules_after_change (window);
1222 g_object_unref (G_OBJECT(header));
1226 g_object_unref(G_OBJECT(header));
1229 if (priv->row_reference) {
1230 gtk_tree_row_reference_free (priv->row_reference);
1233 /* Setup row_reference for the actual msg. */
1234 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1235 if (priv->row_reference == NULL) {
1236 g_warning("No reference for msg header item.");
1240 /* Now set up next_row_reference. */
1241 if (priv->next_row_reference) {
1242 gtk_tree_row_reference_free (priv->next_row_reference);
1245 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1246 select_next_valid_row (priv->header_model,
1247 &(priv->next_row_reference), FALSE, priv->is_outbox);
1249 /* Connect the remaining callbacks to become able to detect
1250 * changes in header-view. */
1251 priv->row_changed_handler =
1252 g_signal_connect (priv->header_model, "row-changed",
1253 G_CALLBACK (modest_msg_view_window_on_row_changed),
1255 priv->row_deleted_handler =
1256 g_signal_connect (priv->header_model, "row-deleted",
1257 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1259 priv->rows_reordered_handler =
1260 g_signal_connect (priv->header_model, "rows-reordered",
1261 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1264 check_dimming_rules_after_change (window);
1268 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1272 ModestMsgViewWindow *window)
1274 ModestMsgViewWindowPrivate *priv = NULL;
1275 gboolean already_changed = FALSE;
1277 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1279 /* If the current row was reordered select the proper next
1280 valid row. The same if the next row reference changes */
1281 if (priv->row_reference &&
1282 gtk_tree_row_reference_valid (priv->row_reference)) {
1284 path = gtk_tree_row_reference_get_path (priv->row_reference);
1285 if (gtk_tree_path_compare (path, arg1) == 0) {
1286 if (priv->next_row_reference) {
1287 gtk_tree_row_reference_free (priv->next_row_reference);
1289 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1290 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1291 already_changed = TRUE;
1293 gtk_tree_path_free (path);
1295 if (!already_changed &&
1296 priv->next_row_reference &&
1297 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1299 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1300 if (gtk_tree_path_compare (path, arg1) == 0) {
1301 if (priv->next_row_reference) {
1302 gtk_tree_row_reference_free (priv->next_row_reference);
1304 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1305 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1307 gtk_tree_path_free (path);
1309 check_dimming_rules_after_change (window);
1312 /* The modest_msg_view_window_update_model_replaced implements update
1313 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1314 * actually belongs to the header-view is the same as the TnyFolder of
1315 * the message of msg-view or not. If they are different, there is
1316 * nothing to do. If they are the same, then the model has replaced and
1317 * the reference in msg-view shall be replaced from the old model to
1318 * the new model. In this case the view will be detached from it's
1319 * header folder. From this point the next/prev buttons are dimmed.
1322 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1323 GtkTreeModel *model,
1324 const gchar *tny_folder_id)
1326 ModestMsgViewWindowPrivate *priv = NULL;
1327 ModestMsgViewWindow *window = NULL;
1329 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1330 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1332 window = MODEST_MSG_VIEW_WINDOW(observer);
1333 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1335 /* If there is an other folder in the header-view then we do
1336 * not care about it's model (msg list). Else if the
1337 * header-view shows the folder the msg shown by us is in, we
1338 * shall replace our model reference and make some check. */
1339 if(model == NULL || tny_folder_id == NULL ||
1340 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1343 /* Model is changed(replaced), so we should forget the old
1344 * one. Because there might be other references and there
1345 * might be some change on the model even if we unreferenced
1346 * it, we need to disconnect our signals here. */
1347 if (priv->header_model) {
1348 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1349 priv->row_changed_handler))
1350 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1351 priv->row_changed_handler);
1352 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1353 priv->row_deleted_handler))
1354 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1355 priv->row_deleted_handler);
1356 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1357 priv->row_inserted_handler))
1358 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1359 priv->row_inserted_handler);
1360 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1361 priv->rows_reordered_handler))
1362 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1363 priv->rows_reordered_handler);
1366 if (priv->row_reference)
1367 gtk_tree_row_reference_free (priv->row_reference);
1368 if (priv->next_row_reference)
1369 gtk_tree_row_reference_free (priv->next_row_reference);
1370 g_object_unref(priv->header_model);
1373 priv->row_changed_handler = 0;
1374 priv->row_deleted_handler = 0;
1375 priv->row_inserted_handler = 0;
1376 priv->rows_reordered_handler = 0;
1377 priv->next_row_reference = NULL;
1378 priv->row_reference = NULL;
1379 priv->header_model = NULL;
1382 priv->header_model = g_object_ref (model);
1384 /* Also we must connect to the new model for row insertions.
1385 * Only for insertions now. We will need other ones only after
1386 * the msg is show by msg-view is added to the new model. */
1387 priv->row_inserted_handler =
1388 g_signal_connect (priv->header_model, "row-inserted",
1389 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1392 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1393 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1397 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1399 ModestMsgViewWindowPrivate *priv= NULL;
1401 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1402 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1404 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1408 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1410 ModestMsgViewWindowPrivate *priv= NULL;
1412 TnyHeader *header = NULL;
1413 GtkTreePath *path = NULL;
1416 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1417 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1419 /* If the message was not obtained from a treemodel,
1420 * for instance if it was opened directly by the search UI:
1422 if (priv->header_model == NULL ||
1423 priv->row_reference == NULL ||
1424 !gtk_tree_row_reference_valid (priv->row_reference)) {
1425 msg = modest_msg_view_window_get_message (self);
1427 header = tny_msg_get_header (msg);
1428 g_object_unref (msg);
1433 /* Get iter of the currently selected message in the header view: */
1434 path = gtk_tree_row_reference_get_path (priv->row_reference);
1435 g_return_val_if_fail (path != NULL, NULL);
1436 gtk_tree_model_get_iter (priv->header_model,
1440 /* Get current message header */
1441 gtk_tree_model_get (priv->header_model, &iter,
1442 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1445 gtk_tree_path_free (path);
1450 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1452 ModestMsgViewWindowPrivate *priv;
1454 g_return_val_if_fail (self, NULL);
1456 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1458 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1462 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1464 ModestMsgViewWindowPrivate *priv;
1466 g_return_val_if_fail (self, NULL);
1468 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1470 return (const gchar*) priv->msg_uid;
1474 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1477 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1478 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1479 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1483 is_active = gtk_toggle_action_get_active (toggle);
1486 gtk_widget_show (priv->find_toolbar);
1487 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1489 gtk_widget_hide (priv->find_toolbar);
1490 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1493 /* update the toggle buttons status */
1494 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1495 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1499 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1500 ModestMsgViewWindow *obj)
1502 GtkToggleAction *toggle;
1503 ModestWindowPrivate *parent_priv;
1504 ModestMsgViewWindowPrivate *priv;
1506 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1507 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1509 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1510 gtk_toggle_action_set_active (toggle, FALSE);
1511 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1515 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1516 ModestMsgViewWindow *obj)
1518 gchar *current_search;
1519 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1521 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1522 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1526 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1528 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1529 g_free (current_search);
1530 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1534 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1536 g_free (priv->last_search);
1537 priv->last_search = g_strdup (current_search);
1538 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1541 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1542 g_free (priv->last_search);
1543 priv->last_search = NULL;
1545 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1546 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1549 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1550 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1551 g_free (priv->last_search);
1552 priv->last_search = NULL;
1554 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1555 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1559 g_free (current_search);
1564 modest_msg_view_window_get_zoom (ModestWindow *window)
1566 ModestMsgViewWindowPrivate *priv;
1568 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1570 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1571 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1575 modest_msg_view_window_key_event (GtkWidget *window,
1581 focus = gtk_window_get_focus (GTK_WINDOW (window));
1583 /* for the find toolbar case */
1584 if (focus && GTK_IS_ENTRY (focus)) {
1585 if (event->keyval == GDK_BackSpace) {
1587 copy = gdk_event_copy ((GdkEvent *) event);
1588 gtk_widget_event (focus, copy);
1589 gdk_event_free (copy);
1594 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1595 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1596 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1597 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1598 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1599 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1600 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1601 /* gboolean return_value; */
1603 if (event->type == GDK_KEY_PRESS) {
1604 GtkScrollType scroll_type;
1606 switch (event->keyval) {
1609 scroll_type = GTK_SCROLL_STEP_UP; break;
1612 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1614 case GDK_KP_Page_Up:
1615 scroll_type = GTK_SCROLL_PAGE_UP; break;
1617 case GDK_KP_Page_Down:
1618 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1621 scroll_type = GTK_SCROLL_START; break;
1624 scroll_type = GTK_SCROLL_END; break;
1625 default: scroll_type = GTK_SCROLL_NONE;
1628 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1629 /* scroll_type, FALSE, &return_value); */
1640 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1643 ModestMsgViewWindowPrivate *priv;
1644 GtkTreeIter tmp_iter;
1645 gboolean is_last_selected;
1647 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1648 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1650 /*if no model (so no rows at all), then virtually we are the last*/
1651 if (!priv->header_model || !priv->row_reference)
1654 if (!gtk_tree_row_reference_valid (priv->row_reference))
1657 path = gtk_tree_row_reference_get_path (priv->row_reference);
1661 is_last_selected = TRUE;
1662 while (is_last_selected) {
1664 gtk_tree_path_next (path);
1665 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1667 gtk_tree_model_get (priv->header_model, &tmp_iter,
1668 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1671 if (msg_is_visible (header, priv->is_outbox))
1672 is_last_selected = FALSE;
1673 g_object_unref(G_OBJECT(header));
1676 gtk_tree_path_free (path);
1677 return is_last_selected;
1681 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1683 ModestMsgViewWindowPrivate *priv;
1685 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1686 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1688 return priv->header_model != NULL;
1692 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1694 ModestMsgViewWindowPrivate *priv;
1696 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1697 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1699 return priv->is_search_result;
1703 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1705 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1707 if (!check_outbox) {
1710 ModestTnySendQueueStatus status;
1711 status = modest_tny_all_send_queues_get_msg_status (header);
1712 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1713 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1718 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1721 ModestMsgViewWindowPrivate *priv;
1722 gboolean is_first_selected;
1723 GtkTreeIter tmp_iter;
1725 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1726 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1728 /*if no model (so no rows at all), then virtually we are the first*/
1729 if (!priv->header_model || !priv->row_reference)
1732 if (!gtk_tree_row_reference_valid (priv->row_reference))
1735 path = gtk_tree_row_reference_get_path (priv->row_reference);
1739 is_first_selected = TRUE;
1740 while (is_first_selected) {
1742 if(!gtk_tree_path_prev (path))
1744 /* Here the 'if' is needless for logic, but let make sure
1745 * iter is valid for gtk_tree_model_get. */
1746 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1748 gtk_tree_model_get (priv->header_model, &tmp_iter,
1749 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1752 if (msg_is_visible (header, priv->is_outbox))
1753 is_first_selected = FALSE;
1754 g_object_unref(G_OBJECT(header));
1757 gtk_tree_path_free (path);
1758 return is_first_selected;
1763 GtkTreeRowReference *row_reference;
1767 message_reader_performer (gboolean canceled,
1769 GtkWindow *parent_window,
1770 TnyAccount *account,
1773 ModestMailOperation *mail_op = NULL;
1774 MsgReaderInfo *info;
1776 info = (MsgReaderInfo *) user_data;
1777 if (canceled || err) {
1781 /* Register the header - it'll be unregistered in the callback */
1782 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1784 /* New mail operation */
1785 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1786 modest_ui_actions_disk_operations_error_handler,
1789 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1790 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1791 g_object_unref (mail_op);
1793 /* Update dimming rules */
1794 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1795 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1798 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1799 g_object_unref (info->header);
1800 g_slice_free (MsgReaderInfo, info);
1805 * Reads the message whose summary item is @header. It takes care of
1806 * several things, among others:
1808 * If the message was not previously downloaded then ask the user
1809 * before downloading. If there is no connection launch the connection
1810 * dialog. Update toolbar dimming rules.
1812 * Returns: TRUE if the mail operation was started, otherwise if the
1813 * user do not want to download the message, or if the user do not
1814 * want to connect, then the operation is not issued
1817 message_reader (ModestMsgViewWindow *window,
1818 ModestMsgViewWindowPrivate *priv,
1820 GtkTreeRowReference *row_reference)
1822 ModestWindowMgr *mgr;
1823 TnyAccount *account;
1825 MsgReaderInfo *info;
1827 g_return_val_if_fail (row_reference != NULL, FALSE);
1829 mgr = modest_runtime_get_window_mgr ();
1830 /* Msg download completed */
1831 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1832 /* Ask the user if he wants to download the message if
1834 if (!tny_device_is_online (modest_runtime_get_device())) {
1835 GtkResponseType response;
1837 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1838 _("mcen_nc_get_msg"));
1839 if (response == GTK_RESPONSE_CANCEL)
1842 folder = tny_header_get_folder (header);
1843 info = g_slice_new (MsgReaderInfo);
1844 info->header = g_object_ref (header);
1845 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1847 /* Offer the connection dialog if necessary */
1848 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1850 TNY_FOLDER_STORE (folder),
1851 message_reader_performer,
1853 g_object_unref (folder);
1858 folder = tny_header_get_folder (header);
1859 account = tny_folder_get_account (folder);
1860 info = g_slice_new (MsgReaderInfo);
1861 info->header = g_object_ref (header);
1862 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1864 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1865 g_object_unref (account);
1866 g_object_unref (folder);
1872 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1874 ModestMsgViewWindowPrivate *priv;
1875 GtkTreePath *path= NULL;
1876 GtkTreeIter tmp_iter;
1878 gboolean retval = TRUE;
1879 GtkTreeRowReference *row_reference = NULL;
1881 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1882 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1884 if (!priv->row_reference)
1887 /* Update the next row reference if it's not valid. This could
1888 happen if for example the header which it was pointing to,
1889 was deleted. The best place to do it is in the row-deleted
1890 handler but the tinymail model do not work like the glib
1891 tree models and reports the deletion when the row is still
1893 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1894 if (gtk_tree_row_reference_valid (priv->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), FALSE, priv->is_outbox);
1899 if (priv->next_row_reference)
1900 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1904 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1906 gtk_tree_model_get_iter (priv->header_model,
1909 gtk_tree_path_free (path);
1911 gtk_tree_model_get (priv->header_model, &tmp_iter,
1912 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1915 /* Read the message & show it */
1916 if (!message_reader (window, priv, header, row_reference)) {
1919 gtk_tree_row_reference_free (row_reference);
1922 g_object_unref (header);
1928 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1930 ModestMsgViewWindowPrivate *priv = NULL;
1932 gboolean finished = FALSE;
1933 gboolean retval = FALSE;
1935 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1936 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1938 /* Return inmediatly if there is no header model */
1939 if (!priv->header_model || !priv->row_reference)
1942 path = gtk_tree_row_reference_get_path (priv->row_reference);
1943 while (!finished && gtk_tree_path_prev (path)) {
1947 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1948 gtk_tree_model_get (priv->header_model, &iter,
1949 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1953 if (msg_is_visible (header, priv->is_outbox)) {
1954 GtkTreeRowReference *row_reference;
1955 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1956 /* Read the message & show it */
1957 retval = message_reader (window, priv, header, row_reference);
1958 gtk_tree_row_reference_free (row_reference);
1962 g_object_unref (header);
1966 gtk_tree_path_free (path);
1971 view_msg_cb (ModestMailOperation *mail_op,
1978 ModestMsgViewWindow *self = NULL;
1979 ModestMsgViewWindowPrivate *priv = NULL;
1980 GtkTreeRowReference *row_reference = NULL;
1982 /* Unregister the header (it was registered before creating the mail operation) */
1983 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1985 row_reference = (GtkTreeRowReference *) user_data;
1987 gtk_tree_row_reference_free (row_reference);
1991 /* If there was any error */
1992 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1993 gtk_tree_row_reference_free (row_reference);
1997 /* Get the window */
1998 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1999 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2000 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2002 /* Update the row reference */
2003 if (priv->row_reference != NULL) {
2004 gtk_tree_row_reference_free (priv->row_reference);
2005 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2006 if (priv->next_row_reference != NULL) {
2007 gtk_tree_row_reference_free (priv->next_row_reference);
2009 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2010 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2013 /* Mark header as read */
2014 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2015 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2017 /* Set new message */
2018 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2019 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2020 modest_msg_view_window_update_priority (self);
2021 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2022 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2025 /* Set the new message uid of the window */
2026 if (priv->msg_uid) {
2027 g_free (priv->msg_uid);
2028 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2031 /* Notify the observers */
2032 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2033 0, priv->header_model, priv->row_reference);
2036 g_object_unref (self);
2037 gtk_tree_row_reference_free (row_reference);
2041 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2043 ModestMsgViewWindowPrivate *priv;
2045 TnyFolderType folder_type;
2047 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2049 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2051 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2055 folder = tny_msg_get_folder (msg);
2057 folder_type = modest_tny_folder_guess_folder_type (folder);
2058 g_object_unref (folder);
2060 g_object_unref (msg);
2068 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2070 ModestMsgViewWindowPrivate *priv;
2071 TnyHeader *header = NULL;
2072 TnyHeaderFlags flags = 0;
2074 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2076 if (priv->header_model && priv->row_reference) {
2078 GtkTreePath *path = NULL;
2080 path = gtk_tree_row_reference_get_path (priv->row_reference);
2081 g_return_if_fail (path != NULL);
2082 gtk_tree_model_get_iter (priv->header_model,
2084 gtk_tree_row_reference_get_path (priv->row_reference));
2086 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2088 gtk_tree_path_free (path);
2091 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2093 header = tny_msg_get_header (msg);
2094 g_object_unref (msg);
2099 flags = tny_header_get_flags (header);
2100 g_object_unref(G_OBJECT(header));
2103 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2108 toolbar_resize (ModestMsgViewWindow *self)
2110 ModestMsgViewWindowPrivate *priv = NULL;
2111 ModestWindowPrivate *parent_priv = NULL;
2113 gint static_button_size;
2114 ModestWindowMgr *mgr;
2116 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2117 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2118 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2120 mgr = modest_runtime_get_window_mgr ();
2121 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2123 if (parent_priv->toolbar) {
2124 /* left size buttons */
2125 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2126 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2127 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2128 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2129 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2130 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2131 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2132 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2133 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2134 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2135 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2136 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2137 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2138 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2139 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2140 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2142 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2143 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2144 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2145 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2146 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2147 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2148 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2149 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2155 modest_msg_view_window_show_toolbar (ModestWindow *self,
2156 gboolean show_toolbar)
2158 ModestMsgViewWindowPrivate *priv = NULL;
2159 ModestWindowPrivate *parent_priv;
2160 GtkWidget *reply_button = NULL, *menu = NULL;
2161 GtkWidget *placeholder = NULL;
2164 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2165 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2167 /* Set optimized view status */
2168 priv->optimized_view = !show_toolbar;
2170 if (!parent_priv->toolbar) {
2171 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2173 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2175 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2176 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2177 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2178 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2179 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2181 /* Add ProgressBar (Transfer toolbar) */
2182 priv->progress_bar = modest_progress_bar_new ();
2183 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2184 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2185 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2186 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2187 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2189 /* Connect cancel 'clicked' signal to abort progress mode */
2190 g_signal_connect(priv->cancel_toolitem, "clicked",
2191 G_CALLBACK(cancel_progressbar),
2194 /* Add it to the observers list */
2195 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2198 hildon_window_add_toolbar (HILDON_WINDOW (self),
2199 GTK_TOOLBAR (parent_priv->toolbar));
2201 /* Set reply button tap and hold menu */
2202 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2203 "/ToolBar/ToolbarMessageReply");
2204 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2205 "/ToolbarReplyCSM");
2206 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2210 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2211 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2212 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2214 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2215 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2216 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2218 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2221 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2222 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2227 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2229 ModestMsgViewWindow *window)
2231 if (!GTK_WIDGET_VISIBLE (window))
2234 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2238 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2240 ModestMsgViewWindowPrivate *priv;
2242 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2243 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2245 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2249 cancel_progressbar (GtkToolButton *toolbutton,
2250 ModestMsgViewWindow *self)
2253 ModestMsgViewWindowPrivate *priv;
2255 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2257 /* Get operation observers and cancel its current operation */
2258 tmp = priv->progress_widgets;
2260 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2261 tmp=g_slist_next(tmp);
2265 observers_empty (ModestMsgViewWindow *self)
2268 ModestMsgViewWindowPrivate *priv;
2269 gboolean is_empty = TRUE;
2270 guint pending_ops = 0;
2272 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2273 tmp = priv->progress_widgets;
2275 /* Check all observers */
2276 while (tmp && is_empty) {
2277 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2278 is_empty = pending_ops == 0;
2280 tmp = g_slist_next(tmp);
2287 on_account_removed (TnyAccountStore *account_store,
2288 TnyAccount *account,
2291 /* Do nothing if it's a transport account, because we only
2292 show the messages of a store account */
2293 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2294 const gchar *parent_acc = NULL;
2295 const gchar *our_acc = NULL;
2297 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2298 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2300 /* Close this window if I'm showing a message of the removed account */
2301 if (strcmp (parent_acc, our_acc) == 0)
2302 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2307 on_mail_operation_started (ModestMailOperation *mail_op,
2310 ModestMsgViewWindow *self;
2311 ModestMailOperationTypeOperation op_type;
2313 ModestMsgViewWindowPrivate *priv;
2314 GObject *source = NULL;
2316 self = MODEST_MSG_VIEW_WINDOW (user_data);
2317 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2318 op_type = modest_mail_operation_get_type_operation (mail_op);
2319 tmp = priv->progress_widgets;
2320 source = modest_mail_operation_get_source(mail_op);
2321 if (G_OBJECT (self) == source) {
2322 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2323 set_toolbar_transfer_mode(self);
2325 modest_progress_object_add_operation (
2326 MODEST_PROGRESS_OBJECT (tmp->data),
2328 tmp = g_slist_next (tmp);
2332 g_object_unref (source);
2336 on_mail_operation_finished (ModestMailOperation *mail_op,
2339 ModestMsgViewWindow *self;
2340 ModestMailOperationTypeOperation op_type;
2342 ModestMsgViewWindowPrivate *priv;
2344 self = MODEST_MSG_VIEW_WINDOW (user_data);
2345 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2346 op_type = modest_mail_operation_get_type_operation (mail_op);
2347 tmp = priv->progress_widgets;
2349 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2351 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2353 tmp = g_slist_next (tmp);
2356 /* If no more operations are being observed, NORMAL mode is enabled again */
2357 if (observers_empty (self)) {
2358 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2361 /* Update dimming rules. We have to do this right here
2362 and not in view_msg_cb because at that point the
2363 transfer mode is still enabled so the dimming rule
2364 won't let the user delete the message that has been
2365 readed for example */
2366 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2367 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2372 on_queue_changed (ModestMailOperationQueue *queue,
2373 ModestMailOperation *mail_op,
2374 ModestMailOperationQueueNotification type,
2375 ModestMsgViewWindow *self)
2377 ModestMsgViewWindowPrivate *priv;
2379 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2381 /* If this operations was created by another window, do nothing */
2382 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2385 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2386 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2388 "operation-started",
2389 G_CALLBACK (on_mail_operation_started),
2391 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2393 "operation-finished",
2394 G_CALLBACK (on_mail_operation_finished),
2396 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2397 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2399 "operation-started");
2400 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2402 "operation-finished");
2407 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2409 ModestMsgViewWindowPrivate *priv;
2410 TnyList *selected_attachments = NULL;
2412 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2413 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2415 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2417 return selected_attachments;
2423 guint banner_idle_id;
2424 } DecodeAsyncHelper;
2427 decode_async_banner_idle (gpointer user_data)
2429 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2431 helper->banner_idle_id = 0;
2432 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2433 g_object_ref (helper->banner);
2439 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2445 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2447 if (helper->banner_idle_id > 0) {
2448 g_source_remove (helper->banner_idle_id);
2449 helper->banner_idle_id = 0;
2451 if (helper->banner) {
2452 gtk_widget_destroy (helper->banner);
2454 if (cancelled || err) {
2455 modest_platform_information_banner (NULL, NULL,
2456 _("mail_ib_file_operation_failed"));
2460 /* make the file read-only */
2461 g_chmod(helper->filepath, 0444);
2463 /* Activate the file */
2464 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2468 g_free (helper->filepath);
2469 g_object_unref (helper->banner);
2470 g_slice_free (DecodeAsyncHelper, helper);
2474 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2475 TnyMimePart *mime_part)
2477 ModestMsgViewWindowPrivate *priv;
2478 const gchar *msg_uid;
2479 gchar *attachment_uid = NULL;
2480 gint attachment_index = 0;
2481 TnyList *attachments;
2483 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2484 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2485 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2487 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2488 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2489 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2490 g_object_unref (attachments);
2492 if (msg_uid && attachment_index >= 0) {
2493 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2496 if (mime_part == NULL) {
2497 gboolean error = FALSE;
2498 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2499 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2501 } else if (tny_list_get_length (selected_attachments) > 1) {
2502 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2506 iter = tny_list_create_iterator (selected_attachments);
2507 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2508 g_object_unref (iter);
2510 g_object_unref (selected_attachments);
2515 g_object_ref (mime_part);
2518 if (tny_mime_part_is_purged (mime_part)) {
2519 g_object_unref (mime_part);
2523 if (!modest_tny_mime_part_is_msg (mime_part)) {
2524 gchar *filepath = NULL;
2525 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2526 gboolean show_error_banner = FALSE;
2527 TnyFsStream *temp_stream = NULL;
2528 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2531 if (temp_stream != NULL) {
2532 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2533 helper->filepath = g_strdup (filepath);
2534 helper->banner = NULL;
2535 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2536 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2537 on_decode_to_stream_async_handler,
2540 g_object_unref (temp_stream);
2541 /* NOTE: files in the temporary area will be automatically
2542 * cleaned after some time if they are no longer in use */
2545 const gchar *content_type;
2546 /* the file may already exist but it isn't writable,
2547 * let's try to open it anyway */
2548 content_type = tny_mime_part_get_content_type (mime_part);
2549 modest_platform_activate_file (filepath, content_type);
2551 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2552 show_error_banner = TRUE;
2557 if (show_error_banner)
2558 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2560 /* message attachment */
2561 TnyHeader *header = NULL;
2562 ModestWindowMgr *mgr;
2563 ModestWindow *msg_win = NULL;
2566 header = tny_msg_get_header (TNY_MSG (mime_part));
2567 mgr = modest_runtime_get_window_mgr ();
2568 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2571 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2572 * thus, we don't do anything */
2573 g_warning ("window for is already being created");
2575 /* it's not found, so create a new window for it */
2576 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2577 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2579 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2580 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2581 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2582 modest_window_get_zoom (MODEST_WINDOW (window)));
2583 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2584 gtk_widget_show_all (GTK_WIDGET (msg_win));
2587 g_object_unref (mime_part);
2600 GnomeVFSResult result;
2603 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2604 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2605 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2606 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2609 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2613 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2614 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2615 g_free (pair->filename);
2616 g_object_unref (pair->part);
2617 g_slice_free (SaveMimePartPair, pair);
2619 g_list_free (info->pairs);
2622 gtk_widget_destroy (info->banner);
2623 g_slice_free (SaveMimePartInfo, info);
2628 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2630 if (info->pairs != NULL) {
2631 save_mime_part_to_file (info);
2633 /* This is a GDK lock because we are an idle callback and
2634 * hildon_banner_show_information is or does Gtk+ code */
2636 gdk_threads_enter (); /* CHECKED */
2637 save_mime_part_info_free (info, TRUE);
2638 if (info->result == GNOME_VFS_OK) {
2639 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2640 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2641 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2642 "cerm_device_memory_full"));
2644 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2646 gdk_threads_leave (); /* CHECKED */
2653 save_mime_part_to_file (SaveMimePartInfo *info)
2655 GnomeVFSHandle *handle;
2657 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2659 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2660 if (info->result == GNOME_VFS_OK) {
2661 GError *error = NULL;
2662 stream = tny_vfs_stream_new (handle);
2663 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2664 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2666 info->result = GNOME_VFS_ERROR_IO;
2668 g_object_unref (G_OBJECT (stream));
2669 g_object_unref (pair->part);
2670 g_slice_free (SaveMimePartPair, pair);
2671 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2673 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2674 save_mime_part_info_free (info, FALSE);
2677 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2682 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2684 gboolean is_ok = TRUE;
2685 gint replaced_files = 0;
2686 const GList *files = info->pairs;
2689 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2690 SaveMimePartPair *pair = iter->data;
2691 if (modest_utils_file_exists (pair->filename)) {
2695 if (replaced_files) {
2696 GtkWidget *confirm_overwrite_dialog;
2697 const gchar *message = (replaced_files == 1) ?
2698 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2699 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2700 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2703 gtk_widget_destroy (confirm_overwrite_dialog);
2707 save_mime_part_info_free (info, TRUE);
2709 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2710 _CS("sfil_ib_saving"));
2711 info->banner = banner;
2712 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2718 save_attachments_response (GtkDialog *dialog,
2722 TnyList *mime_parts;
2724 GList *files_to_save = NULL;
2726 mime_parts = TNY_LIST (user_data);
2728 if (arg1 != GTK_RESPONSE_OK)
2731 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2733 if (!modest_utils_folder_writable (chooser_uri)) {
2734 hildon_banner_show_information
2735 (NULL, NULL, dgettext("hildon-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, TnyList *mime_parts)
2783 ModestMsgViewWindowPrivate *priv;
2784 GtkWidget *save_dialog = NULL;
2785 gchar *folder = NULL;
2786 gchar *filename = NULL;
2787 gchar *save_multiple_str = NULL;
2789 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2790 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2792 if (mime_parts == NULL) {
2793 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2794 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2797 g_object_ref (mime_parts);
2800 /* prepare dialog */
2801 if (tny_list_get_length (mime_parts) == 1) {
2803 /* only one attachment selected */
2804 iter = tny_list_create_iterator (mime_parts);
2805 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2806 g_object_unref (iter);
2807 if (!modest_tny_mime_part_is_msg (mime_part) &&
2808 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2809 !tny_mime_part_is_purged (mime_part)) {
2810 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2812 /* TODO: show any error? */
2813 g_warning ("Tried to save a non-file attachment");
2814 g_object_unref (mime_parts);
2817 g_object_unref (mime_part);
2819 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2820 tny_list_get_length (mime_parts));
2823 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2824 GTK_FILE_CHOOSER_ACTION_SAVE);
2827 folder = g_build_filename (g_get_home_dir (), g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2828 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2833 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2838 /* if multiple, set multiple string */
2839 if (save_multiple_str) {
2840 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2841 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2844 /* We must run this asynchronously, because the hildon dialog
2845 performs a gtk_dialog_run by itself which leads to gdk
2847 g_signal_connect (save_dialog, "response",
2848 G_CALLBACK (save_attachments_response), mime_parts);
2850 gtk_widget_show_all (save_dialog);
2854 show_remove_attachment_information (gpointer userdata)
2856 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2857 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2859 /* We're outside the main lock */
2860 gdk_threads_enter ();
2862 if (priv->remove_attachment_banner != NULL) {
2863 gtk_widget_destroy (priv->remove_attachment_banner);
2864 g_object_unref (priv->remove_attachment_banner);
2867 priv->remove_attachment_banner = g_object_ref (
2868 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2870 gdk_threads_leave ();
2876 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2878 ModestMsgViewWindowPrivate *priv;
2879 TnyList *mime_parts = NULL;
2880 gchar *confirmation_message;
2886 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2887 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2890 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2892 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2894 /* Remove already purged messages from mime parts list */
2895 iter = tny_list_create_iterator (mime_parts);
2896 while (!tny_iterator_is_done (iter)) {
2897 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2898 tny_iterator_next (iter);
2899 if (tny_mime_part_is_purged (part)) {
2900 tny_list_remove (mime_parts, (GObject *) part);
2902 g_object_unref (part);
2904 g_object_unref (iter);
2906 if (tny_list_get_length (mime_parts) == 0) {
2907 g_object_unref (mime_parts);
2911 n_attachments = tny_list_get_length (mime_parts);
2912 if (n_attachments == 1) {
2916 iter = tny_list_create_iterator (mime_parts);
2917 part = (TnyMimePart *) tny_iterator_get_current (iter);
2918 g_object_unref (iter);
2919 if (modest_tny_mime_part_is_msg (part)) {
2921 header = tny_msg_get_header (TNY_MSG (part));
2922 filename = tny_header_dup_subject (header);
2923 g_object_unref (header);
2924 if (filename == NULL)
2925 filename = g_strdup (_("mail_va_no_subject"));
2927 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2929 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2931 g_object_unref (part);
2933 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2934 "mcen_nc_purge_files_text",
2935 n_attachments), n_attachments);
2937 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2938 confirmation_message);
2939 g_free (confirmation_message);
2941 if (response != GTK_RESPONSE_OK) {
2942 g_object_unref (mime_parts);
2946 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2948 iter = tny_list_create_iterator (mime_parts);
2949 while (!tny_iterator_is_done (iter)) {
2952 part = (TnyMimePart *) tny_iterator_get_current (iter);
2953 tny_mime_part_set_purged (TNY_MIME_PART (part));
2954 g_object_unref (part);
2955 tny_iterator_next (iter);
2957 g_object_unref (iter);
2959 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2960 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2961 tny_msg_rewrite_cache (msg);
2962 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2963 g_object_unref (msg);
2965 g_object_unref (mime_parts);
2967 if (priv->purge_timeout > 0) {
2968 g_source_remove (priv->purge_timeout);
2969 priv->purge_timeout = 0;
2972 if (priv->remove_attachment_banner) {
2973 gtk_widget_destroy (priv->remove_attachment_banner);
2974 g_object_unref (priv->remove_attachment_banner);
2975 priv->remove_attachment_banner = NULL;
2983 update_window_title (ModestMsgViewWindow *window)
2985 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2987 TnyHeader *header = NULL;
2988 gchar *subject = NULL;
2990 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2993 header = tny_msg_get_header (msg);
2994 subject = tny_header_dup_subject (header);
2995 g_object_unref (header);
2996 g_object_unref (msg);
2999 if ((subject == NULL)||(subject[0] == '\0')) {
3001 subject = g_strdup (_("mail_va_no_subject"));
3004 gtk_window_set_title (GTK_WINDOW (window), subject);
3008 static void on_move_focus (GtkWidget *widget,
3009 GtkDirectionType direction,
3012 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3016 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3018 GnomeVFSResult result;
3019 GnomeVFSHandle *handle = NULL;
3020 GnomeVFSFileInfo *info = NULL;
3023 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3024 if (result != GNOME_VFS_OK) {
3029 info = gnome_vfs_file_info_new ();
3030 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3031 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3032 /* We put a "safe" default size for going to cache */
3033 *expected_size = (300*1024);
3035 *expected_size = info->size;
3037 gnome_vfs_file_info_unref (info);
3039 stream = tny_vfs_stream_new (handle);
3048 TnyStream *output_stream;
3049 GtkWidget *msg_view;
3053 on_fetch_image_idle_refresh_view (gpointer userdata)
3056 FetchImageData *fidata = (FetchImageData *) userdata;
3057 g_message ("REFRESH VIEW");
3058 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3059 g_message ("QUEUING DRAW");
3060 gtk_widget_queue_draw (fidata->msg_view);
3062 g_object_unref (fidata->msg_view);
3063 g_slice_free (FetchImageData, fidata);
3068 on_fetch_image_thread (gpointer userdata)
3070 FetchImageData *fidata = (FetchImageData *) userdata;
3071 TnyStreamCache *cache;
3072 TnyStream *cache_stream;
3074 cache = modest_runtime_get_images_cache ();
3075 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3076 g_free (fidata->cache_id);
3077 g_free (fidata->uri);
3079 if (cache_stream != NULL) {
3080 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3081 tny_stream_close (cache_stream);
3082 g_object_unref (cache_stream);
3085 tny_stream_close (fidata->output_stream);
3086 g_object_unref (fidata->output_stream);
3089 gdk_threads_enter ();
3090 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3091 gdk_threads_leave ();
3097 on_fetch_image (ModestMsgView *msgview,
3100 ModestMsgViewWindow *window)
3102 const gchar *current_account;
3103 ModestMsgViewWindowPrivate *priv;
3104 FetchImageData *fidata;
3106 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3108 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3110 fidata = g_slice_new0 (FetchImageData);
3111 fidata->msg_view = g_object_ref (msgview);
3112 fidata->uri = g_strdup (uri);
3113 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3114 fidata->output_stream = g_object_ref (stream);
3116 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3117 g_object_unref (fidata->output_stream);
3118 g_free (fidata->cache_id);
3119 g_free (fidata->uri);
3120 g_object_unref (fidata->msg_view);
3121 g_slice_free (FetchImageData, fidata);
3122 tny_stream_close (stream);
3130 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3132 ModestMsgViewWindowPrivate *priv;
3133 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3134 GSList *recipients = NULL;
3136 gboolean contacts_to_add = FALSE;
3138 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3139 if (msg == NULL) return;
3140 recipients = modest_tny_msg_get_all_recipients_list (msg);
3142 if (recipients != NULL) {
3143 GtkWidget *picker_dialog;
3144 GtkWidget *selector;
3148 selector = hildon_touch_selector_new_text ();
3149 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3150 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3151 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3152 (const gchar *) node->data);
3153 contacts_to_add = TRUE;
3157 if (contacts_to_add) {
3159 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3160 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3162 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3163 HILDON_TOUCH_SELECTOR (selector));
3165 gtk_dialog_run (GTK_DIALOG (picker_dialog));
3166 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3167 gtk_widget_destroy (picker_dialog);
3170 modest_address_book_add_address (selected);
3175 g_object_unref (selector);
3180 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3181 g_object_unref (msg);