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 "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include "modest-ui-dimming-manager.h"
54 #include <gdk/gdkkeysyms.h>
55 #include <modest-tny-account.h>
56 #include <modest-mime-part-view.h>
57 #include <modest-isearch-view.h>
58 #include <modest-tny-mime-part.h>
61 #include <glib/gstdio.h>
62 #include <modest-debug.h>
64 #define DEFAULT_FOLDER "MyDocs/.documents"
66 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
67 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
68 static void modest_header_view_observer_init(
69 ModestHeaderViewObserverIface *iface_class);
70 static void modest_msg_view_window_finalize (GObject *obj);
71 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
73 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
74 ModestMsgViewWindow *obj);
75 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
76 ModestMsgViewWindow *obj);
78 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
79 static void modest_msg_view_window_set_zoom (ModestWindow *window,
81 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
82 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
83 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
84 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
87 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
88 GdkEventWindowState *event,
90 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
92 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
93 gboolean show_toolbar);
95 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
97 ModestMsgViewWindow *window);
98 void modest_msg_view_window_on_row_changed(
99 GtkTreeModel *header_model,
102 ModestMsgViewWindow *window);
104 void modest_msg_view_window_on_row_deleted(
105 GtkTreeModel *header_model,
107 ModestMsgViewWindow *window);
109 void modest_msg_view_window_on_row_inserted(
110 GtkTreeModel *header_model,
111 GtkTreePath *tree_path,
112 GtkTreeIter *tree_iter,
113 ModestMsgViewWindow *window);
115 void modest_msg_view_window_on_row_reordered(
116 GtkTreeModel *header_model,
120 ModestMsgViewWindow *window);
122 void modest_msg_view_window_update_model_replaced(
123 ModestHeaderViewObserver *window,
125 const gchar *tny_folder_id);
127 static void cancel_progressbar (GtkToolButton *toolbutton,
128 ModestMsgViewWindow *self);
130 static void on_queue_changed (ModestMailOperationQueue *queue,
131 ModestMailOperation *mail_op,
132 ModestMailOperationQueueNotification type,
133 ModestMsgViewWindow *self);
135 static void on_account_removed (TnyAccountStore *account_store,
139 static void on_move_focus (GtkWidget *widget,
140 GtkDirectionType direction,
143 static void view_msg_cb (ModestMailOperation *mail_op,
150 static void set_toolbar_mode (ModestMsgViewWindow *self,
151 ModestToolBarModes mode);
153 static void update_window_title (ModestMsgViewWindow *window);
155 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
156 static void init_window (ModestMsgViewWindow *obj);
158 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
161 /* list my signals */
167 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
168 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
169 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
172 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
173 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
174 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
175 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
176 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
177 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
178 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
181 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
182 struct _ModestMsgViewWindowPrivate {
185 GtkWidget *main_scroll;
186 GtkWidget *find_toolbar;
189 /* Progress observers */
190 GtkWidget *progress_bar;
191 GSList *progress_widgets;
194 GtkWidget *progress_toolitem;
195 GtkWidget *cancel_toolitem;
196 GtkWidget *prev_toolitem;
197 GtkWidget *next_toolitem;
198 ModestToolBarModes current_toolbar_mode;
200 /* Optimized view enabled */
201 gboolean optimized_view;
203 /* Whether this was created via the *_new_for_search_result() function. */
204 gboolean is_search_result;
206 /* Whether the message is in outbox */
209 /* A reference to the @model of the header view
210 * to allow selecting previous/next messages,
211 * if the message is currently selected in the header view.
213 const gchar *header_folder_id;
214 GtkTreeModel *header_model;
215 GtkTreeRowReference *row_reference;
216 GtkTreeRowReference *next_row_reference;
218 gulong clipboard_change_handler;
219 gulong queue_change_handler;
220 gulong account_removed_handler;
221 gulong row_changed_handler;
222 gulong row_deleted_handler;
223 gulong row_inserted_handler;
224 gulong rows_reordered_handler;
227 GtkWidget *remove_attachment_banner;
229 guint progress_bar_timeout;
236 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
237 MODEST_TYPE_MSG_VIEW_WINDOW, \
238 ModestMsgViewWindowPrivate))
240 static GtkWindowClass *parent_class = NULL;
242 /* uncomment the following if you have defined any signals */
243 static guint signals[LAST_SIGNAL] = {0};
246 modest_msg_view_window_get_type (void)
248 static GType my_type = 0;
250 static const GTypeInfo my_info = {
251 sizeof(ModestMsgViewWindowClass),
252 NULL, /* base init */
253 NULL, /* base finalize */
254 (GClassInitFunc) modest_msg_view_window_class_init,
255 NULL, /* class finalize */
256 NULL, /* class data */
257 sizeof(ModestMsgViewWindow),
259 (GInstanceInitFunc) modest_msg_view_window_init,
262 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
263 "ModestMsgViewWindow",
266 static const GInterfaceInfo modest_header_view_observer_info =
268 (GInterfaceInitFunc) modest_header_view_observer_init,
269 NULL, /* interface_finalize */
270 NULL /* interface_data */
273 g_type_add_interface_static (my_type,
274 MODEST_TYPE_HEADER_VIEW_OBSERVER,
275 &modest_header_view_observer_info);
281 save_state (ModestWindow *self)
283 modest_widget_memory_save (modest_runtime_get_conf (),
285 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
290 restore_settings (ModestMsgViewWindow *self)
293 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
296 conf = modest_runtime_get_conf ();
297 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
298 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
299 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
300 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
301 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
302 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
303 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
304 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
305 modest_widget_memory_restore (conf,
307 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
311 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
313 GObjectClass *gobject_class;
314 ModestWindowClass *modest_window_class;
315 gobject_class = (GObjectClass*) klass;
316 modest_window_class = (ModestWindowClass *) klass;
318 parent_class = g_type_class_peek_parent (klass);
319 gobject_class->finalize = modest_msg_view_window_finalize;
321 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
322 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
323 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
324 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
325 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
326 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
328 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
330 modest_window_class->save_state_func = save_state;
332 signals[MSG_CHANGED_SIGNAL] =
333 g_signal_new ("msg-changed",
334 G_TYPE_FROM_CLASS (gobject_class),
336 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
338 modest_marshal_VOID__POINTER_POINTER,
339 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
342 static void modest_header_view_observer_init(
343 ModestHeaderViewObserverIface *iface_class)
345 iface_class->update_func = modest_msg_view_window_update_model_replaced;
349 modest_msg_view_window_init (ModestMsgViewWindow *obj)
351 ModestMsgViewWindowPrivate *priv;
352 ModestWindowPrivate *parent_priv = NULL;
353 GtkActionGroup *action_group = NULL;
354 GError *error = NULL;
355 GdkPixbuf *window_icon;
357 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
358 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
359 parent_priv->ui_manager = gtk_ui_manager_new();
361 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
362 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
364 /* Add common actions */
365 gtk_action_group_add_actions (action_group,
366 modest_action_entries,
367 G_N_ELEMENTS (modest_action_entries),
369 gtk_action_group_add_toggle_actions (action_group,
370 modest_toggle_action_entries,
371 G_N_ELEMENTS (modest_toggle_action_entries),
373 gtk_action_group_add_toggle_actions (action_group,
374 msg_view_toggle_action_entries,
375 G_N_ELEMENTS (msg_view_toggle_action_entries),
377 gtk_action_group_add_radio_actions (action_group,
378 msg_view_zoom_action_entries,
379 G_N_ELEMENTS (msg_view_zoom_action_entries),
381 G_CALLBACK (modest_ui_actions_on_change_zoom),
384 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
385 g_object_unref (action_group);
387 /* Load the UI definition */
388 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
391 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
392 g_error_free (error);
397 /* Add accelerators */
398 gtk_window_add_accel_group (GTK_WINDOW (obj),
399 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
401 priv->is_search_result = FALSE;
402 priv->is_outbox = FALSE;
404 priv->msg_view = NULL;
405 priv->header_model = NULL;
406 priv->header_folder_id = NULL;
407 priv->clipboard_change_handler = 0;
408 priv->queue_change_handler = 0;
409 priv->account_removed_handler = 0;
410 priv->row_changed_handler = 0;
411 priv->row_deleted_handler = 0;
412 priv->row_inserted_handler = 0;
413 priv->rows_reordered_handler = 0;
414 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
416 priv->optimized_view = FALSE;
417 priv->progress_bar_timeout = 0;
418 priv->purge_timeout = 0;
419 priv->remove_attachment_banner = NULL;
420 priv->msg_uid = NULL;
422 priv->sighandlers = NULL;
425 init_window (MODEST_MSG_VIEW_WINDOW(obj));
427 /* Set window icon */
428 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
430 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
431 g_object_unref (window_icon);
434 hildon_program_add_window (hildon_program_get_instance(),
437 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
438 GTK_WINDOW(obj),"applications_email_viewer");
443 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
445 ModestMsgViewWindowPrivate *priv = NULL;
447 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
449 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
451 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
453 if (priv->progress_bar_timeout > 0) {
454 g_source_remove (priv->progress_bar_timeout);
455 priv->progress_bar_timeout = 0;
462 set_toolbar_mode (ModestMsgViewWindow *self,
463 ModestToolBarModes mode)
465 ModestWindowPrivate *parent_priv;
466 ModestMsgViewWindowPrivate *priv;
467 /* GtkWidget *widget = NULL; */
469 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
471 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
472 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
474 /* Sets current toolbar mode */
475 priv->current_toolbar_mode = mode;
477 /* Update toolbar dimming state */
478 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
481 case TOOLBAR_MODE_NORMAL:
482 if (priv->progress_toolitem) {
483 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
484 gtk_widget_hide (priv->progress_toolitem);
487 if (priv->progress_bar)
488 gtk_widget_hide (priv->progress_bar);
490 if (priv->cancel_toolitem)
491 gtk_widget_hide (priv->cancel_toolitem);
493 if (priv->prev_toolitem)
494 gtk_widget_show (priv->prev_toolitem);
496 if (priv->next_toolitem)
497 gtk_widget_show (priv->next_toolitem);
499 /* Hide toolbar if optimized view is enabled */
500 if (priv->optimized_view) {
501 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
502 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
506 case TOOLBAR_MODE_TRANSFER:
507 if (priv->prev_toolitem)
508 gtk_widget_hide (priv->prev_toolitem);
510 if (priv->next_toolitem)
511 gtk_widget_hide (priv->next_toolitem);
513 if (priv->progress_bar)
514 gtk_widget_show (priv->progress_bar);
516 if (priv->progress_toolitem) {
517 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
518 gtk_widget_show (priv->progress_toolitem);
521 if (priv->cancel_toolitem)
522 gtk_widget_show (priv->cancel_toolitem);
524 /* Show toolbar if it's hiden (optimized view ) */
525 if (priv->optimized_view) {
526 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
527 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
532 g_return_if_reached ();
539 init_window (ModestMsgViewWindow *obj)
541 GtkWidget *main_vbox;
542 ModestMsgViewWindowPrivate *priv;
544 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
546 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
547 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
548 main_vbox = gtk_vbox_new (FALSE, 6);
550 #ifdef MODEST_USE_MOZEMBED
551 priv->main_scroll = priv->msg_view;
552 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
554 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
555 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
557 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
558 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
559 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
561 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
562 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
564 priv->find_toolbar = hildon_find_toolbar_new (NULL);
565 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
566 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
568 gtk_widget_show_all (GTK_WIDGET(main_vbox));
572 modest_msg_view_window_disconnect_signals (ModestWindow *self)
574 ModestMsgViewWindowPrivate *priv;
575 ModestHeaderView *header_view = NULL;
576 ModestWindow *main_window = NULL;
578 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
580 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
581 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
582 priv->clipboard_change_handler))
583 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
584 priv->clipboard_change_handler);
586 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
587 priv->queue_change_handler))
588 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
589 priv->queue_change_handler);
591 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
592 priv->account_removed_handler))
593 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
594 priv->account_removed_handler);
596 if (priv->header_model) {
597 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
598 priv->row_changed_handler))
599 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
600 priv->row_changed_handler);
602 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
603 priv->row_deleted_handler))
604 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
605 priv->row_deleted_handler);
607 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
608 priv->row_inserted_handler))
609 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
610 priv->row_inserted_handler);
612 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
613 priv->rows_reordered_handler))
614 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
615 priv->rows_reordered_handler);
618 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
619 priv->sighandlers = NULL;
621 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
622 FALSE); /* don't create */
626 header_view = MODEST_HEADER_VIEW(
627 modest_main_window_get_child_widget(
628 MODEST_MAIN_WINDOW(main_window),
629 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
630 if (header_view == NULL)
633 modest_header_view_remove_observer(header_view,
634 MODEST_HEADER_VIEW_OBSERVER(self));
638 modest_msg_view_window_finalize (GObject *obj)
640 ModestMsgViewWindowPrivate *priv;
642 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
644 /* Sanity check: shouldn't be needed, the window mgr should
645 call this function before */
646 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
648 if (priv->header_model != NULL) {
649 g_object_unref (priv->header_model);
650 priv->header_model = NULL;
653 if (priv->progress_bar_timeout > 0) {
654 g_source_remove (priv->progress_bar_timeout);
655 priv->progress_bar_timeout = 0;
658 if (priv->remove_attachment_banner) {
659 gtk_widget_destroy (priv->remove_attachment_banner);
660 g_object_unref (priv->remove_attachment_banner);
661 priv->remove_attachment_banner = NULL;
664 if (priv->purge_timeout > 0) {
665 g_source_remove (priv->purge_timeout);
666 priv->purge_timeout = 0;
669 if (priv->row_reference) {
670 gtk_tree_row_reference_free (priv->row_reference);
671 priv->row_reference = NULL;
674 if (priv->next_row_reference) {
675 gtk_tree_row_reference_free (priv->next_row_reference);
676 priv->next_row_reference = NULL;
680 g_free (priv->msg_uid);
681 priv->msg_uid = NULL;
684 G_OBJECT_CLASS(parent_class)->finalize (obj);
688 select_next_valid_row (GtkTreeModel *model,
689 GtkTreeRowReference **row_reference,
692 GtkTreeIter tmp_iter;
694 GtkTreePath *next = NULL;
695 gboolean retval = FALSE;
697 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
699 path = gtk_tree_row_reference_get_path (*row_reference);
700 gtk_tree_model_get_iter (model, &tmp_iter, path);
701 gtk_tree_row_reference_free (*row_reference);
702 *row_reference = NULL;
704 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
705 next = gtk_tree_model_get_path (model, &tmp_iter);
706 *row_reference = gtk_tree_row_reference_new (model, next);
708 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
709 next = gtk_tree_model_get_path (model, &tmp_iter);
711 /* Ensure that we are not selecting the same */
712 if (gtk_tree_path_compare (path, next) != 0) {
713 *row_reference = gtk_tree_row_reference_new (model, next);
719 gtk_tree_path_free (path);
721 gtk_tree_path_free (next);
726 /* TODO: This should be in _init(), with the parameters as properties. */
728 modest_msg_view_window_construct (ModestMsgViewWindow *self,
729 const gchar *modest_account_name,
730 const gchar *msg_uid)
733 ModestMsgViewWindowPrivate *priv = NULL;
734 ModestWindowPrivate *parent_priv = NULL;
735 ModestDimmingRulesGroup *menu_rules_group = NULL;
736 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
737 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
739 obj = G_OBJECT (self);
740 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
741 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
743 priv->msg_uid = g_strdup (msg_uid);
746 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
747 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
748 gtk_widget_show (parent_priv->menubar);
749 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
751 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
752 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
753 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
755 /* Add common dimming rules */
756 modest_dimming_rules_group_add_rules (menu_rules_group,
757 modest_msg_view_menu_dimming_entries,
758 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
759 MODEST_WINDOW (self));
760 modest_dimming_rules_group_add_rules (toolbar_rules_group,
761 modest_msg_view_toolbar_dimming_entries,
762 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
763 MODEST_WINDOW (self));
764 modest_dimming_rules_group_add_rules (clipboard_rules_group,
765 modest_msg_view_clipboard_dimming_entries,
766 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
767 MODEST_WINDOW (self));
769 /* Insert dimming rules group for this window */
770 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
771 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
772 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
773 g_object_unref (menu_rules_group);
774 g_object_unref (toolbar_rules_group);
775 g_object_unref (clipboard_rules_group);
777 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
779 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
781 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);
782 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
783 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
784 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
785 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
786 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
787 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
788 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
789 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
790 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
791 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
793 g_signal_connect (G_OBJECT (obj), "key-release-event",
794 G_CALLBACK (modest_msg_view_window_key_event),
797 g_signal_connect (G_OBJECT (obj), "key-press-event",
798 G_CALLBACK (modest_msg_view_window_key_event),
801 g_signal_connect (G_OBJECT (obj), "window-state-event",
802 G_CALLBACK (modest_msg_view_window_window_state_event),
805 g_signal_connect (G_OBJECT (obj), "move-focus",
806 G_CALLBACK (on_move_focus), obj);
808 /* Mail Operation Queue */
809 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
811 G_CALLBACK (on_queue_changed),
814 /* Account manager */
815 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
817 G_CALLBACK(on_account_removed),
820 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
822 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
823 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
824 priv->last_search = NULL;
826 /* Init the clipboard actions dim status */
827 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
829 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
834 /* FIXME: parameter checks */
836 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
837 const gchar *modest_account_name,
838 const gchar *msg_uid,
840 GtkTreeRowReference *row_reference)
842 ModestMsgViewWindow *window = NULL;
843 ModestMsgViewWindowPrivate *priv = NULL;
844 TnyFolder *header_folder = NULL;
845 ModestHeaderView *header_view = NULL;
846 ModestWindow *main_window = NULL;
847 ModestWindowMgr *mgr = NULL;
850 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
853 mgr = modest_runtime_get_window_mgr ();
854 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
855 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
857 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
859 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
861 /* Remember the message list's TreeModel so we can detect changes
862 * and change the list selection when necessary: */
864 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
866 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
867 MODEST_MAIN_WINDOW(main_window),
868 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
871 if (header_view != NULL){
872 header_folder = modest_header_view_get_folder(header_view);
873 /* This could happen if the header folder was
874 unseleted before opening this msg window (for
875 example if the user selects an account in the
876 folder view of the main window */
878 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
879 priv->header_folder_id = tny_folder_get_id(header_folder);
880 g_assert(priv->header_folder_id != NULL);
881 g_object_unref(header_folder);
885 priv->header_model = g_object_ref(model);
887 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
888 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
889 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
891 priv->row_reference = NULL;
892 priv->next_row_reference = NULL;
895 priv->row_changed_handler = g_signal_connect(
896 GTK_TREE_MODEL(model), "row-changed",
897 G_CALLBACK(modest_msg_view_window_on_row_changed),
899 priv->row_deleted_handler = g_signal_connect(
900 GTK_TREE_MODEL(model), "row-deleted",
901 G_CALLBACK(modest_msg_view_window_on_row_deleted),
903 priv->row_inserted_handler = g_signal_connect (
904 GTK_TREE_MODEL(model), "row-inserted",
905 G_CALLBACK(modest_msg_view_window_on_row_inserted),
907 priv->rows_reordered_handler = g_signal_connect(
908 GTK_TREE_MODEL(model), "rows-reordered",
909 G_CALLBACK(modest_msg_view_window_on_row_reordered),
912 if (header_view != NULL){
913 modest_header_view_add_observer(header_view,
914 MODEST_HEADER_VIEW_OBSERVER(window));
917 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
918 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
919 gtk_widget_show_all (GTK_WIDGET (window));
920 modest_msg_view_window_update_priority (window);
922 /* Check dimming rules */
923 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
924 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
925 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
927 return MODEST_WINDOW(window);
931 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
932 const gchar *modest_account_name,
933 const gchar *msg_uid)
935 ModestMsgViewWindow *window = NULL;
936 ModestMsgViewWindowPrivate *priv = NULL;
937 ModestWindowMgr *mgr = NULL;
939 mgr = modest_runtime_get_window_mgr ();
940 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
941 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
942 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
944 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
946 /* Remember that this is a search result,
947 * so we can disable some UI appropriately: */
948 priv->is_search_result = TRUE;
950 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
952 update_window_title (window);
953 gtk_widget_show_all (GTK_WIDGET (window));
954 modest_msg_view_window_update_priority (window);
956 /* Check dimming rules */
957 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
958 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
959 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
961 return MODEST_WINDOW(window);
965 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
966 const gchar *modest_account_name,
967 const gchar *msg_uid)
970 ModestMsgViewWindowPrivate *priv;
971 ModestWindowMgr *mgr = NULL;
973 g_return_val_if_fail (msg, NULL);
974 mgr = modest_runtime_get_window_mgr ();
975 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
976 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
977 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
978 modest_account_name, msg_uid);
980 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
981 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
983 gtk_widget_show_all (GTK_WIDGET (obj));
985 /* Check dimming rules */
986 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
987 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
988 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
990 return MODEST_WINDOW(obj);
993 void modest_msg_view_window_on_row_changed(
994 GtkTreeModel *header_model,
997 ModestMsgViewWindow *window){
998 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1001 void modest_msg_view_window_on_row_deleted(
1002 GtkTreeModel *header_model,
1004 ModestMsgViewWindow *window){
1005 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1008 /* On insertions we check if the folder still has the message we are
1009 * showing or do not. If do not, we do nothing. Which means we are still
1010 * not attached to any header folder and thus next/prev buttons are
1011 * still dimmed. Once the message that is shown by msg-view is found, the
1012 * new model of header-view will be attached and the references will be set.
1013 * On each further insertions dimming rules will be checked. However
1014 * this requires extra CPU time at least works.
1015 * (An message might be deleted from TnyFolder and thus will not be
1016 * inserted into the model again for example if it is removed by the
1017 * imap server and the header view is refreshed.)
1019 void modest_msg_view_window_on_row_inserted(
1020 GtkTreeModel *new_model,
1021 GtkTreePath *tree_path,
1022 GtkTreeIter *tree_iter,
1023 ModestMsgViewWindow *window){
1024 ModestMsgViewWindowPrivate *priv = NULL;
1025 TnyHeader *header = NULL;
1028 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1030 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1032 /* If we already has a model attached then the message shown by
1033 * msg-view is in it, and thus we do not need any actions but
1034 * to check the dimming rules.*/
1035 if(priv->header_model != NULL){
1036 if (priv->row_reference) {
1037 gtk_tree_row_reference_free(priv->next_row_reference);
1038 priv->next_row_reference = gtk_tree_row_reference_copy(
1039 priv->row_reference);
1040 select_next_valid_row (priv->header_model,
1041 &(priv->next_row_reference), FALSE);
1043 modest_ui_actions_check_toolbar_dimming_rules (
1044 MODEST_WINDOW (window));
1048 /* Check if the newly inserted message is the same we are actually
1049 * showing. IF not, we should remain detached from the header model
1050 * and thus prev and next toolbarbuttons should remain dimmed. */
1051 gtk_tree_model_get (new_model, tree_iter,
1052 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1053 uid = modest_tny_folder_get_header_unique_id(header);
1054 if(!g_str_equal(priv->msg_uid, uid)){
1056 g_object_unref(G_OBJECT(header));
1060 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1061 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1062 g_object_unref(G_OBJECT(header));
1066 /* Setup row_reference for the actual msg. */
1067 priv->row_reference = gtk_tree_row_reference_new(
1068 new_model, tree_path);
1069 if(priv->row_reference == NULL){
1070 g_warning("No reference for msg header item.");
1074 /* Attach new_model and connect some callback to it to become able
1075 * to detect changes in header-view. */
1076 priv->header_model = g_object_ref(new_model);
1077 priv->row_changed_handler = g_signal_connect (new_model, "row-changed",
1078 G_CALLBACK (modest_msg_view_window_on_row_changed),
1080 priv->row_deleted_handler = g_signal_connect (new_model, "row-deleted",
1081 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1083 priv->rows_reordered_handler = g_signal_connect (new_model, "rows-reordered",
1084 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1087 /* Now set up next_row_reference. */
1088 priv->next_row_reference = gtk_tree_row_reference_copy(
1089 priv->row_reference);
1090 select_next_valid_row (priv->header_model,
1091 &(priv->next_row_reference), FALSE);
1093 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1096 void modest_msg_view_window_on_row_reordered(
1097 GtkTreeModel *header_model,
1101 ModestMsgViewWindow *window){
1102 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1105 /* The modest_msg_view_window_update_model_replaced implements update
1106 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1107 * actually belongs to the header-view is the same as the TnyFolder of
1108 * the message of msg-view or not. If they are different, there is
1109 * nothing to do. If they are the same, then the model has replaced and
1110 * the reference in msg-view shall be replaced from the old model to
1111 * the new model. In this case the view will be detached from it's
1112 * header folder. From this point the next/prev buttons are dimmed.
1115 modest_msg_view_window_update_model_replaced(ModestHeaderViewObserver *observer,
1116 GtkTreeModel *model,
1117 const gchar *tny_folder_id){
1118 ModestMsgViewWindowPrivate *priv = NULL;
1119 ModestMsgViewWindow *window = NULL;
1121 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1122 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1124 window = MODEST_MSG_VIEW_WINDOW(observer);
1125 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1127 /* If there is an other folder in the header-view then we do
1128 * not care about it's model (msg list). Else if the
1129 * header-view shows the folder the msg shown by us is in, we
1130 * shall replace our model reference and make some check. */
1131 if(tny_folder_id == NULL ||
1132 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1135 /* Model is changed(replaced), so we should forget the old
1136 * one. Because there might be other references and there
1137 * might be some change on the model even if we unreferenced
1138 * it, we need to disconnect our signals here. */
1139 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1140 priv->row_changed_handler))
1141 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1142 priv->row_changed_handler);
1143 priv->row_changed_handler = 0;
1144 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1145 priv->row_deleted_handler))
1146 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1147 priv->row_deleted_handler);
1148 priv->row_deleted_handler = 0;
1149 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1150 priv->row_inserted_handler))
1151 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1152 priv->row_inserted_handler);
1153 priv->row_inserted_handler = 0;
1154 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1155 priv->rows_reordered_handler))
1156 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1157 priv->rows_reordered_handler);
1158 priv->rows_reordered_handler = 0;
1159 g_object_unref(priv->header_model);
1160 priv->header_model = NULL;
1161 if (priv->row_reference)
1162 gtk_tree_row_reference_free (priv->row_reference);
1163 priv->row_reference = NULL;
1164 if (priv->next_row_reference)
1165 gtk_tree_row_reference_free (priv->next_row_reference);
1166 priv->next_row_reference = NULL;
1168 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1170 g_assert(model != NULL);
1172 /* Also we must connect to the new model for row insertions.
1173 * Only for insertions now. We will need other ones only after
1174 * the msg is show by msg-view is added to the new model. */
1175 priv->row_inserted_handler = g_signal_connect (
1176 model, "row-inserted",
1177 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1182 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1184 ModestMsgViewWindowPrivate *priv= NULL;
1186 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1187 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1189 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1193 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1195 ModestMsgViewWindowPrivate *priv= NULL;
1197 TnyHeader *header = NULL;
1198 GtkTreePath *path = NULL;
1201 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1202 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1204 /* If the message was not obtained from a treemodel,
1205 * for instance if it was opened directly by the search UI:
1207 if (priv->header_model == NULL ||
1208 priv->row_reference == NULL ||
1209 !gtk_tree_row_reference_valid (priv->row_reference)) {
1210 msg = modest_msg_view_window_get_message (self);
1212 header = tny_msg_get_header (msg);
1213 g_object_unref (msg);
1218 /* Get iter of the currently selected message in the header view: */
1219 path = gtk_tree_row_reference_get_path (priv->row_reference);
1220 g_return_val_if_fail (path != NULL, NULL);
1221 gtk_tree_model_get_iter (priv->header_model,
1225 /* Get current message header */
1226 gtk_tree_model_get (priv->header_model, &iter,
1227 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1230 gtk_tree_path_free (path);
1235 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1237 ModestMsgViewWindowPrivate *priv;
1239 g_return_val_if_fail (self, NULL);
1241 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1243 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1247 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1249 ModestMsgViewWindowPrivate *priv;
1251 g_return_val_if_fail (self, NULL);
1253 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1255 return (const gchar*) priv->msg_uid;
1259 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1262 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1263 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1264 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1268 is_active = gtk_toggle_action_get_active (toggle);
1271 gtk_widget_show (priv->find_toolbar);
1272 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1274 gtk_widget_hide (priv->find_toolbar);
1277 /* update the toggle buttons status */
1278 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1279 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1280 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1281 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1286 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1287 ModestMsgViewWindow *obj)
1289 GtkToggleAction *toggle;
1290 ModestWindowPrivate *parent_priv;
1291 ModestMsgViewWindowPrivate *priv;
1293 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1294 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1296 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1297 gtk_toggle_action_set_active (toggle, FALSE);
1298 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1302 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1303 ModestMsgViewWindow *obj)
1305 gchar *current_search;
1306 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1308 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1309 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1313 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1315 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1316 g_free (current_search);
1317 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1321 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1323 g_free (priv->last_search);
1324 priv->last_search = g_strdup (current_search);
1325 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1328 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1329 g_free (priv->last_search);
1330 priv->last_search = NULL;
1332 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1333 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1336 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1337 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1338 g_free (priv->last_search);
1339 priv->last_search = NULL;
1341 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1342 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1346 g_free (current_search);
1351 modest_msg_view_window_set_zoom (ModestWindow *window,
1354 ModestMsgViewWindowPrivate *priv;
1355 ModestWindowPrivate *parent_priv;
1356 GtkAction *action = NULL;
1357 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1359 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1361 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1362 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1363 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1365 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1366 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1368 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1372 modest_msg_view_window_get_zoom (ModestWindow *window)
1374 ModestMsgViewWindowPrivate *priv;
1376 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1378 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1379 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1383 modest_msg_view_window_zoom_plus (ModestWindow *window)
1385 ModestWindowPrivate *parent_priv;
1386 GtkRadioAction *zoom_radio_action;
1387 GSList *group, *node;
1389 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1390 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1391 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1393 group = gtk_radio_action_get_group (zoom_radio_action);
1395 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1396 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1400 for (node = group; node != NULL; node = g_slist_next (node)) {
1401 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1402 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1410 modest_msg_view_window_zoom_minus (ModestWindow *window)
1412 ModestWindowPrivate *parent_priv;
1413 GtkRadioAction *zoom_radio_action;
1414 GSList *group, *node;
1416 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1417 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1418 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1420 group = gtk_radio_action_get_group (zoom_radio_action);
1422 for (node = group; node != NULL; node = g_slist_next (node)) {
1423 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1424 if (node->next != NULL) {
1425 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1428 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1438 modest_msg_view_window_key_event (GtkWidget *window,
1444 focus = gtk_window_get_focus (GTK_WINDOW (window));
1446 /* for the find toolbar case */
1447 if (focus && GTK_IS_ENTRY (focus)) {
1448 if (event->keyval == GDK_BackSpace) {
1450 copy = gdk_event_copy ((GdkEvent *) event);
1451 gtk_widget_event (focus, copy);
1452 gdk_event_free (copy);
1457 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1458 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1459 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1460 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1461 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1462 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1463 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1464 gboolean return_value;
1466 if (event->type == GDK_KEY_RELEASE) {
1467 GtkScrollType scroll_type;
1469 switch (event->keyval) {
1472 scroll_type = GTK_SCROLL_STEP_UP; break;
1475 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1477 case GDK_KP_Page_Up:
1478 scroll_type = GTK_SCROLL_PAGE_UP; break;
1480 case GDK_KP_Page_Down:
1481 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1484 scroll_type = GTK_SCROLL_START; break;
1487 scroll_type = GTK_SCROLL_END; break;
1488 default: scroll_type = GTK_SCROLL_NONE;
1491 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",
1492 scroll_type, FALSE, &return_value);
1503 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1506 ModestMsgViewWindowPrivate *priv;
1507 GtkTreeIter tmp_iter;
1508 gboolean is_last_selected;
1510 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1511 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1513 /*if no model (so no rows at all), then virtually we are the last*/
1514 if (!priv->header_model || !priv->row_reference)
1517 path = gtk_tree_row_reference_get_path (priv->row_reference);
1521 is_last_selected = TRUE;
1522 while (is_last_selected) {
1524 gtk_tree_path_next (path);
1525 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1527 gtk_tree_model_get (priv->header_model, &tmp_iter,
1528 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1531 if (msg_is_visible (header, priv->is_outbox))
1532 is_last_selected = FALSE;
1533 g_object_unref(G_OBJECT(header));
1536 gtk_tree_path_free (path);
1537 return is_last_selected;
1541 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1543 ModestMsgViewWindowPrivate *priv;
1545 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1546 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1548 return priv->header_model != NULL;
1552 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1554 ModestMsgViewWindowPrivate *priv;
1556 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1557 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1559 return priv->is_search_result;
1563 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1565 return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1566 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1571 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1574 ModestMsgViewWindowPrivate *priv;
1575 gboolean is_first_selected;
1576 GtkTreeIter tmp_iter;
1577 /* gchar * path_string;*/
1579 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1580 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1582 /*if no model (so no rows at all), then virtually we are the first*/
1583 if (!priv->header_model || !priv->row_reference)
1586 path = gtk_tree_row_reference_get_path (priv->row_reference);
1590 /* path_string = gtk_tree_path_to_string (path);
1591 is_first_selected = strcmp (path_string, "0");
1593 g_free (path_string);
1594 gtk_tree_path_free (path);
1596 return is_first_selected;*/
1598 is_first_selected = TRUE;
1599 while (is_first_selected) {
1601 if(!gtk_tree_path_prev (path))
1603 /* Here the 'if' is needless for logic, but let make sure
1604 * iter is valid for gtk_tree_model_get. */
1605 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1607 gtk_tree_model_get (priv->header_model, &tmp_iter,
1608 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1611 if (msg_is_visible (header, priv->is_outbox))
1612 is_first_selected = FALSE;
1613 g_object_unref(G_OBJECT(header));
1616 gtk_tree_path_free (path);
1617 return is_first_selected;
1622 GtkTreeRowReference *row_reference;
1626 message_reader_performer (gboolean canceled,
1628 GtkWindow *parent_window,
1629 TnyAccount *account,
1632 ModestMailOperation *mail_op = NULL;
1633 MsgReaderInfo *info;
1635 info = (MsgReaderInfo *) user_data;
1636 if (canceled || err) {
1640 /* Register the header - it'll be unregistered in the callback */
1641 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1643 /* New mail operation */
1644 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1645 modest_ui_actions_disk_operations_error_handler,
1648 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1649 modest_mail_operation_get_msg (mail_op, info->header, view_msg_cb, info->row_reference);
1650 g_object_unref (mail_op);
1652 /* Update dimming rules */
1653 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1654 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1657 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1658 g_object_unref (info->header);
1659 g_slice_free (MsgReaderInfo, info);
1664 * Reads the message whose summary item is @header. It takes care of
1665 * several things, among others:
1667 * If the message was not previously downloaded then ask the user
1668 * before downloading. If there is no connection launch the connection
1669 * dialog. Update toolbar dimming rules.
1671 * Returns: TRUE if the mail operation was started, otherwise if the
1672 * user do not want to download the message, or if the user do not
1673 * want to connect, then the operation is not issued
1676 message_reader (ModestMsgViewWindow *window,
1677 ModestMsgViewWindowPrivate *priv,
1679 GtkTreeRowReference *row_reference)
1681 gboolean already_showing = FALSE;
1682 ModestWindow *msg_window = NULL;
1683 ModestWindowMgr *mgr;
1684 TnyAccount *account;
1686 MsgReaderInfo *info;
1688 g_return_val_if_fail (row_reference != NULL, FALSE);
1690 mgr = modest_runtime_get_window_mgr ();
1691 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1692 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1695 gtk_window_present (GTK_WINDOW (msg_window));
1696 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1700 /* Msg download completed */
1701 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1702 /* Ask the user if he wants to download the message if
1704 if (!tny_device_is_online (modest_runtime_get_device())) {
1705 GtkResponseType response;
1707 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1708 _("mcen_nc_get_msg"));
1709 if (response == GTK_RESPONSE_CANCEL)
1712 folder = tny_header_get_folder (header);
1713 info = g_slice_new (MsgReaderInfo);
1714 info->header = g_object_ref (header);
1715 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1717 /* Offer the connection dialog if necessary */
1718 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1720 TNY_FOLDER_STORE (folder),
1721 message_reader_performer,
1723 g_object_unref (folder);
1728 folder = tny_header_get_folder (header);
1729 account = tny_folder_get_account (folder);
1730 info = g_slice_new (MsgReaderInfo);
1731 info->header = g_object_ref (header);
1732 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1734 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1735 g_object_unref (account);
1736 g_object_unref (folder);
1742 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1744 ModestMsgViewWindowPrivate *priv;
1745 GtkTreePath *path= NULL;
1746 GtkTreeIter tmp_iter;
1748 gboolean retval = TRUE;
1749 GtkTreeRowReference *row_reference = NULL;
1751 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1752 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1754 if (!priv->row_reference)
1757 /* Update the next row reference if it's not valid. This could
1758 happen if for example the header which it was pointing to,
1759 was deleted. The best place to do it is in the row-deleted
1760 handler but the tinymail model do not work like the glib
1761 tree models and reports the deletion when the row is still
1763 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1764 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1765 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1766 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1769 if (priv->next_row_reference)
1770 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1774 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1776 gtk_tree_model_get_iter (priv->header_model,
1779 gtk_tree_path_free (path);
1781 gtk_tree_model_get (priv->header_model, &tmp_iter,
1782 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1785 /* Read the message & show it */
1786 if (!message_reader (window, priv, header, row_reference)) {
1789 gtk_tree_row_reference_free (row_reference);
1792 g_object_unref (header);
1798 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1800 ModestMsgViewWindowPrivate *priv = NULL;
1802 gboolean finished = FALSE;
1803 gboolean retval = FALSE;
1805 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1806 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1808 /* Return inmediatly if there is no header model */
1809 if (!priv->header_model || !priv->row_reference)
1812 path = gtk_tree_row_reference_get_path (priv->row_reference);
1813 while (!finished && gtk_tree_path_prev (path)) {
1817 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1818 gtk_tree_model_get (priv->header_model, &iter,
1819 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1823 if (msg_is_visible (header, priv->is_outbox)) {
1824 GtkTreeRowReference *row_reference;
1825 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1826 /* Read the message & show it */
1827 retval = message_reader (window, priv, header, row_reference);
1828 gtk_tree_row_reference_free (row_reference);
1832 g_object_unref (header);
1836 gtk_tree_path_free (path);
1841 view_msg_cb (ModestMailOperation *mail_op,
1848 ModestMsgViewWindow *self = NULL;
1849 ModestMsgViewWindowPrivate *priv = NULL;
1850 GtkTreeRowReference *row_reference = NULL;
1852 /* Unregister the header (it was registered before creating the mail operation) */
1853 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1855 row_reference = (GtkTreeRowReference *) user_data;
1857 gtk_tree_row_reference_free (row_reference);
1861 /* If there was any error */
1862 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1863 gtk_tree_row_reference_free (row_reference);
1867 /* Get the window */
1868 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1869 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1870 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1872 /* Update the row reference */
1873 if (priv->row_reference != NULL) {
1874 gtk_tree_row_reference_free (priv->row_reference);
1875 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1876 if (priv->next_row_reference != NULL) {
1877 gtk_tree_row_reference_free (priv->next_row_reference);
1879 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1880 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1883 /* Mark header as read */
1884 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1885 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1887 /* Set new message */
1888 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1889 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1890 modest_msg_view_window_update_priority (self);
1891 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1892 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1895 /* Set the new message uid of the window */
1896 if (priv->msg_uid) {
1897 g_free (priv->msg_uid);
1898 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1901 /* Notify the observers */
1902 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1903 0, priv->header_model, priv->row_reference);
1906 g_object_unref (self);
1907 gtk_tree_row_reference_free (row_reference);
1911 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1913 ModestMsgViewWindowPrivate *priv;
1915 TnyFolderType folder_type;
1917 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1919 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1921 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1925 folder = tny_msg_get_folder (msg);
1927 folder_type = modest_tny_folder_guess_folder_type (folder);
1928 g_object_unref (folder);
1930 g_object_unref (msg);
1938 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1940 ModestMsgViewWindowPrivate *priv;
1941 TnyHeader *header = NULL;
1942 TnyHeaderFlags flags = 0;
1944 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1946 if (priv->header_model && priv->row_reference) {
1948 GtkTreePath *path = NULL;
1950 path = gtk_tree_row_reference_get_path (priv->row_reference);
1951 g_return_if_fail (path != NULL);
1952 gtk_tree_model_get_iter (priv->header_model,
1954 gtk_tree_row_reference_get_path (priv->row_reference));
1956 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1958 gtk_tree_path_free (path);
1961 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1963 header = tny_msg_get_header (msg);
1964 g_object_unref (msg);
1969 flags = tny_header_get_flags (header);
1970 g_object_unref(G_OBJECT(header));
1973 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1978 toolbar_resize (ModestMsgViewWindow *self)
1980 ModestMsgViewWindowPrivate *priv = NULL;
1981 ModestWindowPrivate *parent_priv = NULL;
1983 gint static_button_size;
1984 ModestWindowMgr *mgr;
1986 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1987 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1988 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1990 mgr = modest_runtime_get_window_mgr ();
1991 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
1993 if (parent_priv->toolbar) {
1994 /* left size buttons */
1995 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
1996 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1997 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1998 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1999 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2000 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2001 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2002 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2003 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2004 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2005 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2006 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2007 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2008 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2009 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2010 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2012 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2013 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2014 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2015 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2016 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2017 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2018 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2019 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2025 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2027 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2028 ModestWindowPrivate *parent_priv;
2029 ModestWindowMgr *mgr;
2030 gboolean is_fullscreen;
2031 GtkAction *fs_toggle_action;
2034 mgr = modest_runtime_get_window_mgr ();
2035 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2037 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2039 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2040 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2041 if (is_fullscreen != active) {
2042 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2044 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2052 modest_msg_view_window_show_toolbar (ModestWindow *self,
2053 gboolean show_toolbar)
2055 ModestMsgViewWindowPrivate *priv = NULL;
2056 ModestWindowPrivate *parent_priv;
2057 GtkWidget *reply_button = NULL, *menu = NULL;
2058 GtkWidget *placeholder = NULL;
2060 const gchar *action_name;
2063 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2064 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2066 /* Set optimized view status */
2067 priv->optimized_view = !show_toolbar;
2069 if (!parent_priv->toolbar) {
2070 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2072 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2074 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2075 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2076 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2077 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2078 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2080 /* Add ProgressBar (Transfer toolbar) */
2081 priv->progress_bar = modest_progress_bar_new ();
2082 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2083 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2084 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2085 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2086 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2088 /* Connect cancel 'clicked' signal to abort progress mode */
2089 g_signal_connect(priv->cancel_toolitem, "clicked",
2090 G_CALLBACK(cancel_progressbar),
2093 /* Add it to the observers list */
2094 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2097 hildon_window_add_toolbar (HILDON_WINDOW (self),
2098 GTK_TOOLBAR (parent_priv->toolbar));
2100 /* Set reply button tap and hold menu */
2101 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2102 "/ToolBar/ToolbarMessageReply");
2103 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2104 "/ToolbarReplyCSM");
2105 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2109 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2110 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2111 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2113 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2114 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2115 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2117 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2120 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2121 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2124 /* Update also the actions (to update the toggles in the
2125 menus), we have to do it manually because some other window
2126 of the same time could have changed it (remember that the
2127 toolbar fullscreen mode is shared by all the windows of the
2129 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2130 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2132 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2134 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2135 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2140 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2142 ModestMsgViewWindow *window)
2144 if (!GTK_WIDGET_VISIBLE (window))
2147 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2151 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2153 ModestMsgViewWindowPrivate *priv;
2155 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2156 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2158 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2162 cancel_progressbar (GtkToolButton *toolbutton,
2163 ModestMsgViewWindow *self)
2166 ModestMsgViewWindowPrivate *priv;
2168 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2170 /* Get operation observers and cancel its current operation */
2171 tmp = priv->progress_widgets;
2173 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2174 tmp=g_slist_next(tmp);
2178 observers_empty (ModestMsgViewWindow *self)
2181 ModestMsgViewWindowPrivate *priv;
2182 gboolean is_empty = TRUE;
2183 guint pending_ops = 0;
2185 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2186 tmp = priv->progress_widgets;
2188 /* Check all observers */
2189 while (tmp && is_empty) {
2190 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2191 is_empty = pending_ops == 0;
2193 tmp = g_slist_next(tmp);
2200 on_account_removed (TnyAccountStore *account_store,
2201 TnyAccount *account,
2204 /* Do nothing if it's a transport account, because we only
2205 show the messages of a store account */
2206 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2207 const gchar *parent_acc = NULL;
2208 const gchar *our_acc = NULL;
2210 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2211 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2213 /* Close this window if I'm showing a message of the removed account */
2214 if (strcmp (parent_acc, our_acc) == 0)
2215 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2220 on_mail_operation_started (ModestMailOperation *mail_op,
2223 ModestMsgViewWindow *self;
2224 ModestMailOperationTypeOperation op_type;
2226 ModestMsgViewWindowPrivate *priv;
2227 GObject *source = NULL;
2229 self = MODEST_MSG_VIEW_WINDOW (user_data);
2230 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2231 op_type = modest_mail_operation_get_type_operation (mail_op);
2232 tmp = priv->progress_widgets;
2233 source = modest_mail_operation_get_source(mail_op);
2234 if (G_OBJECT (self) == source) {
2235 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2236 set_toolbar_transfer_mode(self);
2238 modest_progress_object_add_operation (
2239 MODEST_PROGRESS_OBJECT (tmp->data),
2241 tmp = g_slist_next (tmp);
2245 g_object_unref (source);
2249 on_mail_operation_finished (ModestMailOperation *mail_op,
2252 ModestMsgViewWindow *self;
2253 ModestMailOperationTypeOperation op_type;
2255 ModestMsgViewWindowPrivate *priv;
2257 self = MODEST_MSG_VIEW_WINDOW (user_data);
2258 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2259 op_type = modest_mail_operation_get_type_operation (mail_op);
2260 tmp = priv->progress_widgets;
2262 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2264 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2266 tmp = g_slist_next (tmp);
2269 /* If no more operations are being observed, NORMAL mode is enabled again */
2270 if (observers_empty (self)) {
2271 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2274 /* Update dimming rules. We have to do this right here
2275 and not in view_msg_cb because at that point the
2276 transfer mode is still enabled so the dimming rule
2277 won't let the user delete the message that has been
2278 readed for example */
2279 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2280 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2285 on_queue_changed (ModestMailOperationQueue *queue,
2286 ModestMailOperation *mail_op,
2287 ModestMailOperationQueueNotification type,
2288 ModestMsgViewWindow *self)
2290 ModestMsgViewWindowPrivate *priv;
2292 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2294 /* If this operations was created by another window, do nothing */
2295 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2298 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2299 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2301 "operation-started",
2302 G_CALLBACK (on_mail_operation_started),
2304 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2306 "operation-finished",
2307 G_CALLBACK (on_mail_operation_finished),
2309 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2310 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2312 "operation-started");
2313 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2315 "operation-finished");
2320 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2322 ModestMsgViewWindowPrivate *priv;
2323 TnyList *selected_attachments = NULL;
2325 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2328 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2330 return selected_attachments;
2334 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2336 ModestMsgViewWindowPrivate *priv;
2337 const gchar *msg_uid;
2338 gchar *attachment_uid = NULL;
2339 gint attachment_index = 0;
2340 TnyList *attachments;
2342 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2343 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2344 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2346 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2347 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2348 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2349 g_object_unref (attachments);
2351 if (msg_uid && attachment_index >= 0) {
2352 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2355 if (mime_part == NULL) {
2356 gboolean error = FALSE;
2357 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2358 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2360 } else if (tny_list_get_length (selected_attachments) > 1) {
2361 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2365 iter = tny_list_create_iterator (selected_attachments);
2366 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2367 g_object_unref (iter);
2369 g_object_unref (selected_attachments);
2374 g_object_ref (mime_part);
2377 if (tny_mime_part_is_purged (mime_part)) {
2378 g_object_unref (mime_part);
2382 if (!modest_tny_mime_part_is_msg (mime_part)) {
2383 gchar *filepath = NULL;
2384 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2385 const gchar *content_type;
2386 gboolean show_error_banner = FALSE;
2388 TnyFsStream *temp_stream = NULL;
2389 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2392 if (temp_stream != NULL) {
2393 content_type = tny_mime_part_get_content_type (mime_part);
2394 if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2395 /* make the file read-only */
2396 if (g_chmod(filepath, 0444) != 0)
2397 g_warning ("%s: failed to set file '%s' to read-only: %s",
2398 __FUNCTION__, filepath, strerror(errno));
2400 modest_platform_activate_file (filepath, content_type);
2402 /* error while saving attachment, maybe cerm_device_memory_full */
2403 show_error_banner = TRUE;
2405 g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2409 g_object_unref (temp_stream);
2411 /* NOTE: files in the temporary area will be automatically
2412 * cleaned after some time if they are no longer in use */
2414 if (filepath != NULL) {
2415 /* the file may already exist but it isn't writable,
2416 * let's try to open it anyway */
2417 content_type = tny_mime_part_get_content_type (mime_part);
2418 modest_platform_activate_file (filepath, content_type);
2421 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2422 show_error_banner = TRUE;
2425 if (show_error_banner)
2426 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2428 /* message attachment */
2429 TnyHeader *header = NULL;
2430 ModestWindowMgr *mgr;
2431 ModestWindow *msg_win = NULL;
2434 header = tny_msg_get_header (TNY_MSG (mime_part));
2435 mgr = modest_runtime_get_window_mgr ();
2436 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2439 if (msg_win) /* there is already a window for this uid; top it */
2440 gtk_window_present (GTK_WINDOW(msg_win));
2442 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2443 * thus, we don't do anything */
2444 g_warning ("window for is already being created");
2446 /* it's not found, so create a new window for it */
2447 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2448 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2450 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2451 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2452 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2453 modest_window_get_zoom (MODEST_WINDOW (window)));
2454 modest_window_mgr_register_window (mgr, msg_win);
2455 gtk_widget_show_all (GTK_WIDGET (msg_win));
2458 g_object_unref (mime_part);
2471 GnomeVFSResult result;
2474 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2475 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2476 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2477 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2480 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2484 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2485 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2486 g_free (pair->filename);
2487 g_object_unref (pair->part);
2488 g_slice_free (SaveMimePartPair, pair);
2490 g_list_free (info->pairs);
2493 gtk_widget_destroy (info->banner);
2494 g_slice_free (SaveMimePartInfo, info);
2499 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2501 if (info->pairs != NULL) {
2502 save_mime_part_to_file (info);
2504 /* This is a GDK lock because we are an idle callback and
2505 * hildon_banner_show_information is or does Gtk+ code */
2507 gdk_threads_enter (); /* CHECKED */
2508 save_mime_part_info_free (info, TRUE);
2509 if (info->result == GNOME_VFS_OK) {
2510 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2511 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2512 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2513 "cerm_device_memory_full"));
2515 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2517 gdk_threads_leave (); /* CHECKED */
2524 save_mime_part_to_file (SaveMimePartInfo *info)
2526 GnomeVFSHandle *handle;
2528 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2530 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2531 if (info->result == GNOME_VFS_OK) {
2532 stream = tny_vfs_stream_new (handle);
2533 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2534 info->result = GNOME_VFS_ERROR_IO;
2536 g_object_unref (G_OBJECT (stream));
2537 g_object_unref (pair->part);
2538 g_slice_free (SaveMimePartPair, pair);
2539 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2541 save_mime_part_info_free (info, FALSE);
2544 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2549 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2551 gboolean is_ok = TRUE;
2552 gint replaced_files = 0;
2553 const GList *files = info->pairs;
2556 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2557 SaveMimePartPair *pair = iter->data;
2558 if (modest_utils_file_exists (pair->filename)) {
2562 if (replaced_files) {
2563 GtkWidget *confirm_overwrite_dialog;
2564 const gchar *message = (replaced_files == 1) ?
2565 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2566 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2567 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2570 gtk_widget_destroy (confirm_overwrite_dialog);
2574 save_mime_part_info_free (info, TRUE);
2576 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2577 _CS("sfil_ib_saving"));
2578 info->banner = banner;
2579 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2586 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2588 ModestMsgViewWindowPrivate *priv;
2589 GList *files_to_save = NULL;
2590 GtkWidget *save_dialog = NULL;
2591 gchar *folder = NULL;
2592 const gchar *filename = NULL;
2593 gchar *save_multiple_str = NULL;
2595 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2596 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2598 if (mime_parts == NULL) {
2599 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2600 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2603 g_object_ref (mime_parts);
2606 /* prepare dialog */
2607 if (tny_list_get_length (mime_parts) == 1) {
2609 /* only one attachment selected */
2610 iter = tny_list_create_iterator (mime_parts);
2611 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2612 g_object_unref (iter);
2613 if (!modest_tny_mime_part_is_msg (mime_part) &&
2614 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2615 !tny_mime_part_is_purged (mime_part)) {
2616 filename = tny_mime_part_get_filename (mime_part);
2618 /* TODO: show any error? */
2619 g_warning ("Tried to save a non-file attachment");
2620 g_object_unref (mime_parts);
2623 g_object_unref (mime_part);
2625 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2626 tny_list_get_length (mime_parts));
2629 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2630 GTK_FILE_CHOOSER_ACTION_SAVE);
2633 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2634 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2638 if (filename != NULL)
2639 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2642 /* if multiple, set multiple string */
2643 if (save_multiple_str) {
2644 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2645 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2649 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2650 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2652 if (!modest_utils_folder_writable (chooser_uri)) {
2653 hildon_banner_show_information
2654 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2658 iter = tny_list_create_iterator (mime_parts);
2659 while (!tny_iterator_is_done (iter)) {
2660 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2662 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2663 !tny_mime_part_is_purged (mime_part) &&
2664 (tny_mime_part_get_filename (mime_part) != NULL)) {
2665 SaveMimePartPair *pair;
2667 pair = g_slice_new0 (SaveMimePartPair);
2668 if (save_multiple_str) {
2669 gchar *escaped = gnome_vfs_escape_slashes (
2670 tny_mime_part_get_filename (mime_part));
2671 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2674 pair->filename = g_strdup (chooser_uri);
2676 pair->part = mime_part;
2677 files_to_save = g_list_prepend (files_to_save, pair);
2679 tny_iterator_next (iter);
2681 g_object_unref (iter);
2683 g_free (chooser_uri);
2686 gtk_widget_destroy (save_dialog);
2688 g_object_unref (mime_parts);
2690 if (files_to_save != NULL) {
2691 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2692 info->pairs = files_to_save;
2693 info->result = TRUE;
2694 save_mime_parts_to_file_with_checks (info);
2699 show_remove_attachment_information (gpointer userdata)
2701 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2702 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2704 /* We're outside the main lock */
2705 gdk_threads_enter ();
2707 if (priv->remove_attachment_banner != NULL) {
2708 gtk_widget_destroy (priv->remove_attachment_banner);
2709 g_object_unref (priv->remove_attachment_banner);
2712 priv->remove_attachment_banner = g_object_ref (
2713 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2715 gdk_threads_leave ();
2721 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2723 ModestMsgViewWindowPrivate *priv;
2724 TnyList *mime_parts = NULL;
2725 gchar *confirmation_message;
2730 /* TnyFolder *folder; */
2732 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2733 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2736 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2738 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2740 /* Remove already purged messages from mime parts list */
2741 iter = tny_list_create_iterator (mime_parts);
2742 while (!tny_iterator_is_done (iter)) {
2743 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2744 tny_iterator_next (iter);
2745 if (tny_mime_part_is_purged (part)) {
2746 tny_list_remove (mime_parts, (GObject *) part);
2748 g_object_unref (part);
2750 g_object_unref (iter);
2752 if (tny_list_get_length (mime_parts) == 0) {
2753 g_object_unref (mime_parts);
2757 n_attachments = tny_list_get_length (mime_parts);
2758 if (n_attachments == 1) {
2762 iter = tny_list_create_iterator (mime_parts);
2763 part = (TnyMimePart *) tny_iterator_get_current (iter);
2764 g_object_unref (iter);
2765 if (modest_tny_mime_part_is_msg (part)) {
2767 header = tny_msg_get_header (TNY_MSG (part));
2768 filename = tny_header_dup_subject (header);
2769 g_object_unref (header);
2770 if (filename == NULL)
2771 filename = g_strdup (_("mail_va_no_subject"));
2773 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2775 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2777 g_object_unref (part);
2779 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2780 "mcen_nc_purge_files_text",
2781 n_attachments), n_attachments);
2783 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2784 confirmation_message);
2785 g_free (confirmation_message);
2787 if (response != GTK_RESPONSE_OK) {
2788 g_object_unref (mime_parts);
2792 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2793 /* folder = tny_msg_get_folder (msg); */
2794 /* tny_msg_uncache_attachments (msg); */
2795 /* tny_folder_refresh (folder, NULL); */
2796 /* g_object_unref (folder); */
2798 iter = tny_list_create_iterator (mime_parts);
2799 while (!tny_iterator_is_done (iter)) {
2802 part = (TnyMimePart *) tny_iterator_get_current (iter);
2803 tny_mime_part_set_purged (TNY_MIME_PART (part));
2804 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2805 g_object_unref (part);
2806 tny_iterator_next (iter);
2808 g_object_unref (iter);
2810 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2811 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2812 tny_msg_rewrite_cache (msg);
2813 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2814 g_object_unref (msg);
2816 g_object_unref (mime_parts);
2818 if (priv->purge_timeout > 0) {
2819 g_source_remove (priv->purge_timeout);
2820 priv->purge_timeout = 0;
2823 if (priv->remove_attachment_banner) {
2824 gtk_widget_destroy (priv->remove_attachment_banner);
2825 g_object_unref (priv->remove_attachment_banner);
2826 priv->remove_attachment_banner = NULL;
2834 update_window_title (ModestMsgViewWindow *window)
2836 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2838 TnyHeader *header = NULL;
2839 gchar *subject = NULL;
2841 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2844 header = tny_msg_get_header (msg);
2845 subject = tny_header_dup_subject (header);
2846 g_object_unref (header);
2847 g_object_unref (msg);
2850 if ((subject == NULL)||(subject[0] == '\0')) {
2852 subject = g_strdup (_("mail_va_no_subject"));
2855 gtk_window_set_title (GTK_WINDOW (window), subject);
2859 static void on_move_focus (GtkWidget *widget,
2860 GtkDirectionType direction,
2863 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");