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 modest_msg_view_window_update_priority (window);
955 gtk_widget_show_all (GTK_WIDGET (window));
958 return MODEST_WINDOW(window);
962 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
963 const gchar *modest_account_name,
964 const gchar *msg_uid)
967 ModestMsgViewWindowPrivate *priv;
968 ModestWindowMgr *mgr = NULL;
970 g_return_val_if_fail (msg, NULL);
971 mgr = modest_runtime_get_window_mgr ();
972 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
973 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
974 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
975 modest_account_name, msg_uid);
977 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
978 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
980 gtk_widget_show_all (GTK_WIDGET (obj));
982 /* Check dimming rules */
983 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
984 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
985 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
987 return MODEST_WINDOW(obj);
990 void modest_msg_view_window_on_row_changed(
991 GtkTreeModel *header_model,
994 ModestMsgViewWindow *window){
995 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
998 void modest_msg_view_window_on_row_deleted(
999 GtkTreeModel *header_model,
1001 ModestMsgViewWindow *window){
1002 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1005 /* On insertions we check if the folder still has the message we are
1006 * showing or do not. If do not, we do nothing. Which means we are still
1007 * not attached to any header folder and thus next/prev buttons are
1008 * still dimmed. Once the message that is shown by msg-view is found, the
1009 * new model of header-view will be attached and the references will be set.
1010 * On each further insertions dimming rules will be checked. However
1011 * this requires extra CPU time at least works.
1012 * (An message might be deleted from TnyFolder and thus will not be
1013 * inserted into the model again for example if it is removed by the
1014 * imap server and the header view is refreshed.)
1016 void modest_msg_view_window_on_row_inserted(
1017 GtkTreeModel *new_model,
1018 GtkTreePath *tree_path,
1019 GtkTreeIter *tree_iter,
1020 ModestMsgViewWindow *window){
1021 ModestMsgViewWindowPrivate *priv = NULL;
1022 TnyHeader *header = NULL;
1025 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1027 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1029 /* If we already has a model attached then the message shown by
1030 * msg-view is in it, and thus we do not need any actions but
1031 * to check the dimming rules.*/
1032 if(priv->header_model != NULL){
1033 if (priv->row_reference) {
1034 gtk_tree_row_reference_free(priv->next_row_reference);
1035 priv->next_row_reference = gtk_tree_row_reference_copy(
1036 priv->row_reference);
1037 select_next_valid_row (priv->header_model,
1038 &(priv->next_row_reference), FALSE);
1040 modest_ui_actions_check_toolbar_dimming_rules (
1041 MODEST_WINDOW (window));
1045 /* Check if the newly inserted message is the same we are actually
1046 * showing. IF not, we should remain detached from the header model
1047 * and thus prev and next toolbarbuttons should remain dimmed. */
1048 gtk_tree_model_get (new_model, tree_iter,
1049 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1050 uid = modest_tny_folder_get_header_unique_id(header);
1051 if(!g_str_equal(priv->msg_uid, uid)){
1053 g_object_unref(G_OBJECT(header));
1057 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1058 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1059 g_object_unref(G_OBJECT(header));
1063 /* Setup row_reference for the actual msg. */
1064 priv->row_reference = gtk_tree_row_reference_new(
1065 new_model, tree_path);
1066 if(priv->row_reference == NULL){
1067 g_warning("No reference for msg header item.");
1071 /* Attach new_model and connect some callback to it to become able
1072 * to detect changes in header-view. */
1073 priv->header_model = g_object_ref(new_model);
1074 priv->row_changed_handler = g_signal_connect (new_model, "row-changed",
1075 G_CALLBACK (modest_msg_view_window_on_row_changed),
1077 priv->row_deleted_handler = g_signal_connect (new_model, "row-deleted",
1078 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1080 priv->rows_reordered_handler = g_signal_connect (new_model, "rows-reordered",
1081 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1084 /* Now set up next_row_reference. */
1085 priv->next_row_reference = gtk_tree_row_reference_copy(
1086 priv->row_reference);
1087 select_next_valid_row (priv->header_model,
1088 &(priv->next_row_reference), FALSE);
1090 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1093 void modest_msg_view_window_on_row_reordered(
1094 GtkTreeModel *header_model,
1098 ModestMsgViewWindow *window){
1099 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1102 /* The modest_msg_view_window_update_model_replaced implements update
1103 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1104 * actually belongs to the header-view is the same as the TnyFolder of
1105 * the message of msg-view or not. If they are different, there is
1106 * nothing to do. If they are the same, then the model has replaced and
1107 * the reference in msg-view shall be replaced from the old model to
1108 * the new model. In this case the view will be detached from it's
1109 * header folder. From this point the next/prev buttons are dimmed.
1112 modest_msg_view_window_update_model_replaced(ModestHeaderViewObserver *observer,
1113 GtkTreeModel *model,
1114 const gchar *tny_folder_id){
1115 ModestMsgViewWindowPrivate *priv = NULL;
1116 ModestMsgViewWindow *window = NULL;
1118 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1119 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1121 window = MODEST_MSG_VIEW_WINDOW(observer);
1122 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1124 /* If there is an other folder in the header-view then we do
1125 * not care about it's model (msg list). Else if the
1126 * header-view shows the folder the msg shown by us is in, we
1127 * shall replace our model reference and make some check. */
1128 if(tny_folder_id == NULL ||
1129 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1132 /* Model is changed(replaced), so we should forget the old
1133 * one. Because there might be other references and there
1134 * might be some change on the model even if we unreferenced
1135 * it, we need to disconnect our signals here. */
1136 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1137 priv->row_changed_handler))
1138 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1139 priv->row_changed_handler);
1140 priv->row_changed_handler = 0;
1141 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1142 priv->row_deleted_handler))
1143 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1144 priv->row_deleted_handler);
1145 priv->row_deleted_handler = 0;
1146 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1147 priv->row_inserted_handler))
1148 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1149 priv->row_inserted_handler);
1150 priv->row_inserted_handler = 0;
1151 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1152 priv->rows_reordered_handler))
1153 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1154 priv->rows_reordered_handler);
1155 priv->rows_reordered_handler = 0;
1156 g_object_unref(priv->header_model);
1157 priv->header_model = NULL;
1158 if (priv->row_reference)
1159 gtk_tree_row_reference_free (priv->row_reference);
1160 priv->row_reference = NULL;
1161 if (priv->next_row_reference)
1162 gtk_tree_row_reference_free (priv->next_row_reference);
1163 priv->next_row_reference = NULL;
1165 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1167 g_assert(model != NULL);
1169 /* Also we must connect to the new model for row insertions.
1170 * Only for insertions now. We will need other ones only after
1171 * the msg is show by msg-view is added to the new model. */
1172 priv->row_inserted_handler = g_signal_connect (
1173 model, "row-inserted",
1174 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1179 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1181 ModestMsgViewWindowPrivate *priv= NULL;
1183 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1184 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1186 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1190 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1192 ModestMsgViewWindowPrivate *priv= NULL;
1194 TnyHeader *header = NULL;
1195 GtkTreePath *path = NULL;
1198 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1199 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1201 /* If the message was not obtained from a treemodel,
1202 * for instance if it was opened directly by the search UI:
1204 if (priv->header_model == NULL ||
1205 priv->row_reference == NULL ||
1206 !gtk_tree_row_reference_valid (priv->row_reference)) {
1207 msg = modest_msg_view_window_get_message (self);
1209 header = tny_msg_get_header (msg);
1210 g_object_unref (msg);
1215 /* Get iter of the currently selected message in the header view: */
1216 path = gtk_tree_row_reference_get_path (priv->row_reference);
1217 g_return_val_if_fail (path != NULL, NULL);
1218 gtk_tree_model_get_iter (priv->header_model,
1222 /* Get current message header */
1223 gtk_tree_model_get (priv->header_model, &iter,
1224 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1227 gtk_tree_path_free (path);
1232 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1234 ModestMsgViewWindowPrivate *priv;
1236 g_return_val_if_fail (self, NULL);
1238 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1240 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1244 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1246 ModestMsgViewWindowPrivate *priv;
1248 g_return_val_if_fail (self, NULL);
1250 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1252 return (const gchar*) priv->msg_uid;
1256 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1259 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1260 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1261 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1265 is_active = gtk_toggle_action_get_active (toggle);
1268 gtk_widget_show (priv->find_toolbar);
1269 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1271 gtk_widget_hide (priv->find_toolbar);
1274 /* update the toggle buttons status */
1275 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1276 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1277 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1278 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1283 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1284 ModestMsgViewWindow *obj)
1286 GtkToggleAction *toggle;
1287 ModestWindowPrivate *parent_priv;
1288 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1290 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1291 gtk_toggle_action_set_active (toggle, FALSE);
1295 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1296 ModestMsgViewWindow *obj)
1298 gchar *current_search;
1299 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1301 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1302 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1306 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1308 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1309 g_free (current_search);
1310 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1314 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1316 g_free (priv->last_search);
1317 priv->last_search = g_strdup (current_search);
1318 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1321 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1322 g_free (priv->last_search);
1323 priv->last_search = NULL;
1325 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1326 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1329 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1330 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1331 g_free (priv->last_search);
1332 priv->last_search = NULL;
1334 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1335 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1339 g_free (current_search);
1344 modest_msg_view_window_set_zoom (ModestWindow *window,
1347 ModestMsgViewWindowPrivate *priv;
1348 ModestWindowPrivate *parent_priv;
1349 GtkAction *action = NULL;
1350 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1352 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1354 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1355 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1356 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1358 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1359 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1361 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1365 modest_msg_view_window_get_zoom (ModestWindow *window)
1367 ModestMsgViewWindowPrivate *priv;
1369 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1371 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1372 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1376 modest_msg_view_window_zoom_plus (ModestWindow *window)
1378 ModestWindowPrivate *parent_priv;
1379 GtkRadioAction *zoom_radio_action;
1380 GSList *group, *node;
1382 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1383 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1384 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1386 group = gtk_radio_action_get_group (zoom_radio_action);
1388 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1389 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1393 for (node = group; node != NULL; node = g_slist_next (node)) {
1394 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1395 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1403 modest_msg_view_window_zoom_minus (ModestWindow *window)
1405 ModestWindowPrivate *parent_priv;
1406 GtkRadioAction *zoom_radio_action;
1407 GSList *group, *node;
1409 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1410 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1411 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1413 group = gtk_radio_action_get_group (zoom_radio_action);
1415 for (node = group; node != NULL; node = g_slist_next (node)) {
1416 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1417 if (node->next != NULL) {
1418 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1421 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1431 modest_msg_view_window_key_event (GtkWidget *window,
1437 focus = gtk_window_get_focus (GTK_WINDOW (window));
1439 /* for the find toolbar case */
1440 if (focus && GTK_IS_ENTRY (focus)) {
1441 if (event->keyval == GDK_BackSpace) {
1443 copy = gdk_event_copy ((GdkEvent *) event);
1444 gtk_widget_event (focus, copy);
1445 gdk_event_free (copy);
1450 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1451 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1452 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1453 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1454 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1455 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1456 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1457 gboolean return_value;
1459 if (event->type == GDK_KEY_RELEASE) {
1460 GtkScrollType scroll_type;
1462 switch (event->keyval) {
1465 scroll_type = GTK_SCROLL_STEP_UP; break;
1468 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1470 case GDK_KP_Page_Up:
1471 scroll_type = GTK_SCROLL_PAGE_UP; break;
1473 case GDK_KP_Page_Down:
1474 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1477 scroll_type = GTK_SCROLL_START; break;
1480 scroll_type = GTK_SCROLL_END; break;
1481 default: scroll_type = GTK_SCROLL_NONE;
1484 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",
1485 scroll_type, FALSE, &return_value);
1496 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1499 ModestMsgViewWindowPrivate *priv;
1500 GtkTreeIter tmp_iter;
1501 gboolean is_last_selected;
1503 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1504 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1506 /*if no model (so no rows at all), then virtually we are the last*/
1507 if (!priv->header_model || !priv->row_reference)
1510 path = gtk_tree_row_reference_get_path (priv->row_reference);
1514 is_last_selected = TRUE;
1515 while (is_last_selected) {
1517 gtk_tree_path_next (path);
1518 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1520 gtk_tree_model_get (priv->header_model, &tmp_iter,
1521 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1524 if (msg_is_visible (header, priv->is_outbox))
1525 is_last_selected = FALSE;
1526 g_object_unref(G_OBJECT(header));
1529 gtk_tree_path_free (path);
1530 return is_last_selected;
1534 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1536 ModestMsgViewWindowPrivate *priv;
1538 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1539 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1541 return priv->header_model != NULL;
1545 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1547 ModestMsgViewWindowPrivate *priv;
1549 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1550 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1552 return priv->is_search_result;
1556 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1558 return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1559 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1564 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1567 ModestMsgViewWindowPrivate *priv;
1568 gboolean is_first_selected;
1569 GtkTreeIter tmp_iter;
1570 /* gchar * path_string;*/
1572 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1573 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1575 /*if no model (so no rows at all), then virtually we are the first*/
1576 if (!priv->header_model || !priv->row_reference)
1579 path = gtk_tree_row_reference_get_path (priv->row_reference);
1583 /* path_string = gtk_tree_path_to_string (path);
1584 is_first_selected = strcmp (path_string, "0");
1586 g_free (path_string);
1587 gtk_tree_path_free (path);
1589 return is_first_selected;*/
1591 is_first_selected = TRUE;
1592 while (is_first_selected) {
1594 if(!gtk_tree_path_prev (path))
1596 /* Here the 'if' is needless for logic, but let make sure
1597 * iter is valid for gtk_tree_model_get. */
1598 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1600 gtk_tree_model_get (priv->header_model, &tmp_iter,
1601 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1604 if (msg_is_visible (header, priv->is_outbox))
1605 is_first_selected = FALSE;
1606 g_object_unref(G_OBJECT(header));
1609 gtk_tree_path_free (path);
1610 return is_first_selected;
1615 GtkTreeRowReference *row_reference;
1619 message_reader_performer (gboolean canceled,
1621 GtkWindow *parent_window,
1622 TnyAccount *account,
1625 ModestMailOperation *mail_op = NULL;
1626 MsgReaderInfo *info;
1628 info = (MsgReaderInfo *) user_data;
1629 if (canceled || err) {
1633 /* Register the header - it'll be unregistered in the callback */
1634 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1636 /* New mail operation */
1637 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1638 modest_ui_actions_disk_operations_error_handler,
1641 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1642 modest_mail_operation_get_msg (mail_op, info->header, view_msg_cb, info->row_reference);
1643 g_object_unref (mail_op);
1645 /* Update dimming rules */
1646 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1647 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1650 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1651 g_object_unref (info->header);
1652 g_slice_free (MsgReaderInfo, info);
1657 * Reads the message whose summary item is @header. It takes care of
1658 * several things, among others:
1660 * If the message was not previously downloaded then ask the user
1661 * before downloading. If there is no connection launch the connection
1662 * dialog. Update toolbar dimming rules.
1664 * Returns: TRUE if the mail operation was started, otherwise if the
1665 * user do not want to download the message, or if the user do not
1666 * want to connect, then the operation is not issued
1669 message_reader (ModestMsgViewWindow *window,
1670 ModestMsgViewWindowPrivate *priv,
1672 GtkTreeRowReference *row_reference)
1674 gboolean already_showing = FALSE;
1675 ModestWindow *msg_window = NULL;
1676 ModestWindowMgr *mgr;
1677 TnyAccount *account;
1679 MsgReaderInfo *info;
1681 g_return_val_if_fail (row_reference != NULL, FALSE);
1683 mgr = modest_runtime_get_window_mgr ();
1684 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1685 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1688 gtk_window_present (GTK_WINDOW (msg_window));
1689 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1693 /* Msg download completed */
1694 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1695 /* Ask the user if he wants to download the message if
1697 if (!tny_device_is_online (modest_runtime_get_device())) {
1698 GtkResponseType response;
1700 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1701 _("mcen_nc_get_msg"));
1702 if (response == GTK_RESPONSE_CANCEL)
1705 folder = tny_header_get_folder (header);
1706 info = g_slice_new (MsgReaderInfo);
1707 info->header = g_object_ref (header);
1708 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1710 /* Offer the connection dialog if necessary */
1711 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1713 TNY_FOLDER_STORE (folder),
1714 message_reader_performer,
1716 g_object_unref (folder);
1721 folder = tny_header_get_folder (header);
1722 account = tny_folder_get_account (folder);
1723 info = g_slice_new (MsgReaderInfo);
1724 info->header = g_object_ref (header);
1725 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1727 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1728 g_object_unref (account);
1729 g_object_unref (folder);
1735 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1737 ModestMsgViewWindowPrivate *priv;
1738 GtkTreePath *path= NULL;
1739 GtkTreeIter tmp_iter;
1741 gboolean retval = TRUE;
1742 GtkTreeRowReference *row_reference = NULL;
1744 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1745 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1747 if (!priv->row_reference)
1750 /* Update the next row reference if it's not valid. This could
1751 happen if for example the header which it was pointing to,
1752 was deleted. The best place to do it is in the row-deleted
1753 handler but the tinymail model do not work like the glib
1754 tree models and reports the deletion when the row is still
1756 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1757 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1758 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1759 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1762 if (priv->next_row_reference)
1763 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1767 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1769 gtk_tree_model_get_iter (priv->header_model,
1772 gtk_tree_path_free (path);
1774 gtk_tree_model_get (priv->header_model, &tmp_iter,
1775 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1778 /* Read the message & show it */
1779 if (!message_reader (window, priv, header, row_reference)) {
1782 gtk_tree_row_reference_free (row_reference);
1785 g_object_unref (header);
1791 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1793 ModestMsgViewWindowPrivate *priv = NULL;
1795 gboolean finished = FALSE;
1796 gboolean retval = FALSE;
1798 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1799 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1801 /* Return inmediatly if there is no header model */
1802 if (!priv->header_model || !priv->row_reference)
1805 path = gtk_tree_row_reference_get_path (priv->row_reference);
1806 while (!finished && gtk_tree_path_prev (path)) {
1810 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1811 gtk_tree_model_get (priv->header_model, &iter,
1812 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1816 if (msg_is_visible (header, priv->is_outbox)) {
1817 GtkTreeRowReference *row_reference;
1818 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1819 /* Read the message & show it */
1820 retval = message_reader (window, priv, header, row_reference);
1821 gtk_tree_row_reference_free (row_reference);
1825 g_object_unref (header);
1829 gtk_tree_path_free (path);
1834 view_msg_cb (ModestMailOperation *mail_op,
1841 ModestMsgViewWindow *self = NULL;
1842 ModestMsgViewWindowPrivate *priv = NULL;
1843 GtkTreeRowReference *row_reference = NULL;
1845 /* Unregister the header (it was registered before creating the mail operation) */
1846 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1848 row_reference = (GtkTreeRowReference *) user_data;
1850 gtk_tree_row_reference_free (row_reference);
1854 /* If there was any error */
1855 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1856 gtk_tree_row_reference_free (row_reference);
1860 /* Get the window */
1861 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1862 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1863 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1865 /* Update the row reference */
1866 if (priv->row_reference != NULL) {
1867 gtk_tree_row_reference_free (priv->row_reference);
1868 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1869 if (priv->next_row_reference != NULL) {
1870 gtk_tree_row_reference_free (priv->next_row_reference);
1872 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1873 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1876 /* Mark header as read */
1877 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1878 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1880 /* Set new message */
1881 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1882 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1883 modest_msg_view_window_update_priority (self);
1884 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1885 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1888 /* Set the new message uid of the window */
1889 if (priv->msg_uid) {
1890 g_free (priv->msg_uid);
1891 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1894 /* Notify the observers */
1895 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1896 0, priv->header_model, priv->row_reference);
1899 g_object_unref (self);
1900 gtk_tree_row_reference_free (row_reference);
1904 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1906 ModestMsgViewWindowPrivate *priv;
1908 TnyFolderType folder_type;
1910 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1912 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1914 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1918 folder = tny_msg_get_folder (msg);
1920 folder_type = modest_tny_folder_guess_folder_type (folder);
1921 g_object_unref (folder);
1923 g_object_unref (msg);
1931 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1933 ModestMsgViewWindowPrivate *priv;
1934 TnyHeader *header = NULL;
1935 TnyHeaderFlags flags = 0;
1937 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1939 if (priv->header_model && priv->row_reference) {
1941 GtkTreePath *path = NULL;
1943 path = gtk_tree_row_reference_get_path (priv->row_reference);
1944 g_return_if_fail (path != NULL);
1945 gtk_tree_model_get_iter (priv->header_model,
1947 gtk_tree_row_reference_get_path (priv->row_reference));
1949 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1951 gtk_tree_path_free (path);
1954 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1956 header = tny_msg_get_header (msg);
1957 g_object_unref (msg);
1962 flags = tny_header_get_flags (header);
1963 g_object_unref(G_OBJECT(header));
1966 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1971 toolbar_resize (ModestMsgViewWindow *self)
1973 ModestMsgViewWindowPrivate *priv = NULL;
1974 ModestWindowPrivate *parent_priv = NULL;
1976 gint static_button_size;
1977 ModestWindowMgr *mgr;
1979 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1980 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1981 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1983 mgr = modest_runtime_get_window_mgr ();
1984 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
1986 if (parent_priv->toolbar) {
1987 /* left size buttons */
1988 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
1989 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1990 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1991 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1992 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
1993 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1994 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1995 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1996 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
1997 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1998 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1999 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2000 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2001 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2002 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2003 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2005 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2006 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2007 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2008 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2009 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2010 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2011 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2012 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2018 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2020 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2021 ModestWindowPrivate *parent_priv;
2022 ModestWindowMgr *mgr;
2023 gboolean is_fullscreen;
2024 GtkAction *fs_toggle_action;
2027 mgr = modest_runtime_get_window_mgr ();
2028 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2030 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2032 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2033 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2034 if (is_fullscreen != active) {
2035 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2037 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2045 modest_msg_view_window_show_toolbar (ModestWindow *self,
2046 gboolean show_toolbar)
2048 ModestMsgViewWindowPrivate *priv = NULL;
2049 ModestWindowPrivate *parent_priv;
2050 GtkWidget *reply_button = NULL, *menu = NULL;
2051 GtkWidget *placeholder = NULL;
2053 const gchar *action_name;
2056 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2057 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2059 /* Set optimized view status */
2060 priv->optimized_view = !show_toolbar;
2062 if (!parent_priv->toolbar) {
2063 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2065 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2067 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2068 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2069 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2070 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2071 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2073 /* Add ProgressBar (Transfer toolbar) */
2074 priv->progress_bar = modest_progress_bar_new ();
2075 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2076 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2077 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2078 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2079 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2081 /* Connect cancel 'clicked' signal to abort progress mode */
2082 g_signal_connect(priv->cancel_toolitem, "clicked",
2083 G_CALLBACK(cancel_progressbar),
2086 /* Add it to the observers list */
2087 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2090 hildon_window_add_toolbar (HILDON_WINDOW (self),
2091 GTK_TOOLBAR (parent_priv->toolbar));
2093 /* Set reply button tap and hold menu */
2094 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2095 "/ToolBar/ToolbarMessageReply");
2096 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2097 "/ToolbarReplyCSM");
2098 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2102 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2103 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2104 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2106 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2107 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2108 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2110 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2113 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2114 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2117 /* Update also the actions (to update the toggles in the
2118 menus), we have to do it manually because some other window
2119 of the same time could have changed it (remember that the
2120 toolbar fullscreen mode is shared by all the windows of the
2122 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2123 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2125 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2127 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2128 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2133 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2135 ModestMsgViewWindow *window)
2137 if (!GTK_WIDGET_VISIBLE (window))
2140 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2144 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2146 ModestMsgViewWindowPrivate *priv;
2148 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2149 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2151 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2155 cancel_progressbar (GtkToolButton *toolbutton,
2156 ModestMsgViewWindow *self)
2159 ModestMsgViewWindowPrivate *priv;
2161 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2163 /* Get operation observers and cancel its current operation */
2164 tmp = priv->progress_widgets;
2166 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2167 tmp=g_slist_next(tmp);
2171 observers_empty (ModestMsgViewWindow *self)
2174 ModestMsgViewWindowPrivate *priv;
2175 gboolean is_empty = TRUE;
2176 guint pending_ops = 0;
2178 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2179 tmp = priv->progress_widgets;
2181 /* Check all observers */
2182 while (tmp && is_empty) {
2183 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2184 is_empty = pending_ops == 0;
2186 tmp = g_slist_next(tmp);
2193 on_account_removed (TnyAccountStore *account_store,
2194 TnyAccount *account,
2197 /* Do nothing if it's a transport account, because we only
2198 show the messages of a store account */
2199 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2200 const gchar *parent_acc = NULL;
2201 const gchar *our_acc = NULL;
2203 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2204 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2206 /* Close this window if I'm showing a message of the removed account */
2207 if (strcmp (parent_acc, our_acc) == 0)
2208 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2213 on_mail_operation_started (ModestMailOperation *mail_op,
2216 ModestMsgViewWindow *self;
2217 ModestMailOperationTypeOperation op_type;
2219 ModestMsgViewWindowPrivate *priv;
2220 GObject *source = NULL;
2222 self = MODEST_MSG_VIEW_WINDOW (user_data);
2223 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2224 op_type = modest_mail_operation_get_type_operation (mail_op);
2225 tmp = priv->progress_widgets;
2226 source = modest_mail_operation_get_source(mail_op);
2227 if (G_OBJECT (self) == source) {
2228 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2229 set_toolbar_transfer_mode(self);
2231 modest_progress_object_add_operation (
2232 MODEST_PROGRESS_OBJECT (tmp->data),
2234 tmp = g_slist_next (tmp);
2238 g_object_unref (source);
2242 on_mail_operation_finished (ModestMailOperation *mail_op,
2245 ModestMsgViewWindow *self;
2246 ModestMailOperationTypeOperation op_type;
2248 ModestMsgViewWindowPrivate *priv;
2250 self = MODEST_MSG_VIEW_WINDOW (user_data);
2251 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2252 op_type = modest_mail_operation_get_type_operation (mail_op);
2253 tmp = priv->progress_widgets;
2255 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2257 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2259 tmp = g_slist_next (tmp);
2262 /* If no more operations are being observed, NORMAL mode is enabled again */
2263 if (observers_empty (self)) {
2264 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2267 /* Update dimming rules. We have to do this right here
2268 and not in view_msg_cb because at that point the
2269 transfer mode is still enabled so the dimming rule
2270 won't let the user delete the message that has been
2271 readed for example */
2272 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2273 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2278 on_queue_changed (ModestMailOperationQueue *queue,
2279 ModestMailOperation *mail_op,
2280 ModestMailOperationQueueNotification type,
2281 ModestMsgViewWindow *self)
2283 ModestMsgViewWindowPrivate *priv;
2285 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2287 /* If this operations was created by another window, do nothing */
2288 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2291 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2292 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2294 "operation-started",
2295 G_CALLBACK (on_mail_operation_started),
2297 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2299 "operation-finished",
2300 G_CALLBACK (on_mail_operation_finished),
2302 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2303 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2305 "operation-started");
2306 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2308 "operation-finished");
2313 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2315 ModestMsgViewWindowPrivate *priv;
2316 TnyList *selected_attachments = NULL;
2318 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2319 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2321 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2323 return selected_attachments;
2327 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2329 ModestMsgViewWindowPrivate *priv;
2330 const gchar *msg_uid;
2331 gchar *attachment_uid = NULL;
2332 gint attachment_index = 0;
2333 TnyList *attachments;
2335 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2336 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2337 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2339 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2340 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2341 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2342 g_object_unref (attachments);
2344 if (msg_uid && attachment_index >= 0) {
2345 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2348 if (mime_part == NULL) {
2349 gboolean error = FALSE;
2350 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2351 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2353 } else if (tny_list_get_length (selected_attachments) > 1) {
2354 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2358 iter = tny_list_create_iterator (selected_attachments);
2359 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2360 g_object_unref (iter);
2362 g_object_unref (selected_attachments);
2367 g_object_ref (mime_part);
2370 if (tny_mime_part_is_purged (mime_part)) {
2371 g_object_unref (mime_part);
2375 if (!modest_tny_mime_part_is_msg (mime_part)) {
2376 gchar *filepath = NULL;
2377 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2378 const gchar *content_type;
2379 gboolean show_error_banner = FALSE;
2381 TnyFsStream *temp_stream = NULL;
2382 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2385 if (temp_stream != NULL) {
2386 content_type = tny_mime_part_get_content_type (mime_part);
2387 if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2388 /* make the file read-only */
2389 if (g_chmod(filepath, 0444) != 0)
2390 g_warning ("%s: failed to set file '%s' to read-only: %s",
2391 __FUNCTION__, filepath, strerror(errno));
2393 modest_platform_activate_file (filepath, content_type);
2395 /* error while saving attachment, maybe cerm_device_memory_full */
2396 show_error_banner = TRUE;
2398 g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2402 g_object_unref (temp_stream);
2404 /* NOTE: files in the temporary area will be automatically
2405 * cleaned after some time if they are no longer in use */
2407 if (filepath != NULL) {
2408 /* the file may already exist but it isn't writable,
2409 * let's try to open it anyway */
2410 content_type = tny_mime_part_get_content_type (mime_part);
2411 modest_platform_activate_file (filepath, content_type);
2414 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2415 show_error_banner = TRUE;
2418 if (show_error_banner)
2419 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2421 /* message attachment */
2422 TnyHeader *header = NULL;
2423 ModestWindowMgr *mgr;
2424 ModestWindow *msg_win = NULL;
2427 header = tny_msg_get_header (TNY_MSG (mime_part));
2428 mgr = modest_runtime_get_window_mgr ();
2429 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2432 if (msg_win) /* there is already a window for this uid; top it */
2433 gtk_window_present (GTK_WINDOW(msg_win));
2435 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2436 * thus, we don't do anything */
2437 g_warning ("window for is already being created");
2439 /* it's not found, so create a new window for it */
2440 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2441 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2443 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2444 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2445 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2446 modest_window_get_zoom (MODEST_WINDOW (window)));
2447 modest_window_mgr_register_window (mgr, msg_win);
2448 gtk_widget_show_all (GTK_WIDGET (msg_win));
2451 g_object_unref (mime_part);
2464 GnomeVFSResult result;
2467 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2468 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2469 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2470 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2473 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2477 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2478 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2479 g_free (pair->filename);
2480 g_object_unref (pair->part);
2481 g_slice_free (SaveMimePartPair, pair);
2483 g_list_free (info->pairs);
2486 gtk_widget_destroy (info->banner);
2487 g_slice_free (SaveMimePartInfo, info);
2492 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2494 if (info->pairs != NULL) {
2495 save_mime_part_to_file (info);
2497 /* This is a GDK lock because we are an idle callback and
2498 * hildon_banner_show_information is or does Gtk+ code */
2500 gdk_threads_enter (); /* CHECKED */
2501 save_mime_part_info_free (info, TRUE);
2502 if (info->result == GNOME_VFS_OK) {
2503 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2504 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2505 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2506 "cerm_device_memory_full"));
2508 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2510 gdk_threads_leave (); /* CHECKED */
2517 save_mime_part_to_file (SaveMimePartInfo *info)
2519 GnomeVFSHandle *handle;
2521 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2523 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2524 if (info->result == GNOME_VFS_OK) {
2525 stream = tny_vfs_stream_new (handle);
2526 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2527 info->result = GNOME_VFS_ERROR_IO;
2529 g_object_unref (G_OBJECT (stream));
2530 g_object_unref (pair->part);
2531 g_slice_free (SaveMimePartPair, pair);
2532 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2534 save_mime_part_info_free (info, FALSE);
2537 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2542 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2544 gboolean is_ok = TRUE;
2545 gint replaced_files = 0;
2546 const GList *files = info->pairs;
2549 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2550 SaveMimePartPair *pair = iter->data;
2551 if (modest_utils_file_exists (pair->filename)) {
2555 if (replaced_files) {
2556 GtkWidget *confirm_overwrite_dialog;
2557 const gchar *message = (replaced_files == 1) ?
2558 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2559 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2560 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2563 gtk_widget_destroy (confirm_overwrite_dialog);
2567 save_mime_part_info_free (info, TRUE);
2569 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2570 _CS("sfil_ib_saving"));
2571 info->banner = banner;
2572 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2579 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2581 ModestMsgViewWindowPrivate *priv;
2582 GList *files_to_save = NULL;
2583 GtkWidget *save_dialog = NULL;
2584 gchar *folder = NULL;
2585 const gchar *filename = NULL;
2586 gchar *save_multiple_str = NULL;
2588 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2589 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2591 if (mime_parts == NULL) {
2592 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2593 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2596 g_object_ref (mime_parts);
2599 /* prepare dialog */
2600 if (tny_list_get_length (mime_parts) == 1) {
2602 /* only one attachment selected */
2603 iter = tny_list_create_iterator (mime_parts);
2604 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2605 g_object_unref (iter);
2606 if (!modest_tny_mime_part_is_msg (mime_part) &&
2607 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2608 !tny_mime_part_is_purged (mime_part)) {
2609 filename = tny_mime_part_get_filename (mime_part);
2611 /* TODO: show any error? */
2612 g_warning ("Tried to save a non-file attachment");
2613 g_object_unref (mime_parts);
2616 g_object_unref (mime_part);
2618 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2619 tny_list_get_length (mime_parts));
2622 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2623 GTK_FILE_CHOOSER_ACTION_SAVE);
2626 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2627 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2631 if (filename != NULL)
2632 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2635 /* if multiple, set multiple string */
2636 if (save_multiple_str) {
2637 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2638 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2642 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2643 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2645 if (!modest_utils_folder_writable (chooser_uri)) {
2646 hildon_banner_show_information
2647 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2651 iter = tny_list_create_iterator (mime_parts);
2652 while (!tny_iterator_is_done (iter)) {
2653 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2655 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2656 !tny_mime_part_is_purged (mime_part) &&
2657 (tny_mime_part_get_filename (mime_part) != NULL)) {
2658 SaveMimePartPair *pair;
2660 pair = g_slice_new0 (SaveMimePartPair);
2661 if (save_multiple_str) {
2662 gchar *escaped = gnome_vfs_escape_slashes (
2663 tny_mime_part_get_filename (mime_part));
2664 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2667 pair->filename = g_strdup (chooser_uri);
2669 pair->part = mime_part;
2670 files_to_save = g_list_prepend (files_to_save, pair);
2672 tny_iterator_next (iter);
2674 g_object_unref (iter);
2676 g_free (chooser_uri);
2679 gtk_widget_destroy (save_dialog);
2681 g_object_unref (mime_parts);
2683 if (files_to_save != NULL) {
2684 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2685 info->pairs = files_to_save;
2686 info->result = TRUE;
2687 save_mime_parts_to_file_with_checks (info);
2692 show_remove_attachment_information (gpointer userdata)
2694 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2695 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2697 /* We're outside the main lock */
2698 gdk_threads_enter ();
2700 if (priv->remove_attachment_banner != NULL) {
2701 gtk_widget_destroy (priv->remove_attachment_banner);
2702 g_object_unref (priv->remove_attachment_banner);
2705 priv->remove_attachment_banner = g_object_ref (
2706 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2708 gdk_threads_leave ();
2714 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2716 ModestMsgViewWindowPrivate *priv;
2717 TnyList *mime_parts = NULL;
2718 gchar *confirmation_message;
2723 /* TnyFolder *folder; */
2725 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2726 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2729 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2731 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2733 /* Remove already purged messages from mime parts list */
2734 iter = tny_list_create_iterator (mime_parts);
2735 while (!tny_iterator_is_done (iter)) {
2736 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2737 tny_iterator_next (iter);
2738 if (tny_mime_part_is_purged (part)) {
2739 tny_list_remove (mime_parts, (GObject *) part);
2741 g_object_unref (part);
2743 g_object_unref (iter);
2745 if (tny_list_get_length (mime_parts) == 0) {
2746 g_object_unref (mime_parts);
2750 n_attachments = tny_list_get_length (mime_parts);
2751 if (n_attachments == 1) {
2752 const gchar *filename;
2755 iter = tny_list_create_iterator (mime_parts);
2756 part = (TnyMimePart *) tny_iterator_get_current (iter);
2757 g_object_unref (iter);
2758 if (modest_tny_mime_part_is_msg (part)) {
2760 header = tny_msg_get_header (TNY_MSG (part));
2761 filename = tny_header_get_subject (header);
2762 g_object_unref (header);
2763 if (filename == NULL)
2764 filename = _("mail_va_no_subject");
2766 filename = tny_mime_part_get_filename (TNY_MIME_PART (part));
2768 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2769 g_object_unref (part);
2771 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2772 "mcen_nc_purge_files_text",
2773 n_attachments), n_attachments);
2775 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2776 confirmation_message);
2777 g_free (confirmation_message);
2779 if (response != GTK_RESPONSE_OK) {
2780 g_object_unref (mime_parts);
2784 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2785 /* folder = tny_msg_get_folder (msg); */
2786 /* tny_msg_uncache_attachments (msg); */
2787 /* tny_folder_refresh (folder, NULL); */
2788 /* g_object_unref (folder); */
2790 iter = tny_list_create_iterator (mime_parts);
2791 while (!tny_iterator_is_done (iter)) {
2794 part = (TnyMimePart *) tny_iterator_get_current (iter);
2795 tny_mime_part_set_purged (TNY_MIME_PART (part));
2796 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2797 g_object_unref (part);
2798 tny_iterator_next (iter);
2800 g_object_unref (iter);
2802 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2803 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2804 tny_msg_rewrite_cache (msg);
2805 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2806 g_object_unref (msg);
2808 g_object_unref (mime_parts);
2810 if (priv->purge_timeout > 0) {
2811 g_source_remove (priv->purge_timeout);
2812 priv->purge_timeout = 0;
2815 if (priv->remove_attachment_banner) {
2816 gtk_widget_destroy (priv->remove_attachment_banner);
2817 g_object_unref (priv->remove_attachment_banner);
2818 priv->remove_attachment_banner = NULL;
2826 update_window_title (ModestMsgViewWindow *window)
2828 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2830 TnyHeader *header = NULL;
2831 const gchar *subject = NULL;
2833 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2836 header = tny_msg_get_header (msg);
2837 subject = tny_header_get_subject (header);
2838 g_object_unref (msg);
2841 if ((subject == NULL)||(subject[0] == '\0'))
2842 subject = _("mail_va_no_subject");
2844 gtk_window_set_title (GTK_WINDOW (window), subject);
2848 static void on_move_focus (GtkWidget *widget,
2849 GtkDirectionType direction,
2852 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");