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-attachments-view.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include "modest-progress-bar-widget.h"
52 #include "modest-defs.h"
53 #include "modest-hildon-includes.h"
54 #include "modest-ui-dimming-manager.h"
55 #include <gdk/gdkkeysyms.h>
56 #include <modest-tny-account.h>
57 #include <modest-mime-part-view.h>
58 #include <modest-isearch-view.h>
61 #include <glib/gstdio.h>
63 #define DEFAULT_FOLDER "MyDocs/.documents"
65 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
66 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
67 static void modest_header_view_observer_init(
68 ModestHeaderViewObserverIface *iface_class);
69 static void modest_msg_view_window_finalize (GObject *obj);
70 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
72 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
73 ModestMsgViewWindow *obj);
74 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
75 ModestMsgViewWindow *obj);
77 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
78 static void modest_msg_view_window_set_zoom (ModestWindow *window,
80 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
81 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
82 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
83 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
86 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
87 GdkEventWindowState *event,
89 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
91 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
92 gboolean show_toolbar);
94 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
96 ModestMsgViewWindow *window);
97 void modest_msg_view_window_on_row_changed(
98 GtkTreeModel *header_model,
101 ModestMsgViewWindow *window);
103 void modest_msg_view_window_on_row_deleted(
104 GtkTreeModel *header_model,
106 ModestMsgViewWindow *window);
108 void modest_msg_view_window_on_row_inserted(
109 GtkTreeModel *header_model,
110 GtkTreePath *tree_path,
111 GtkTreeIter *tree_iter,
112 ModestMsgViewWindow *window);
114 void modest_msg_view_window_on_row_reordered(
115 GtkTreeModel *header_model,
119 ModestMsgViewWindow *window);
121 void modest_msg_view_window_update_model_replaced(
122 ModestHeaderViewObserver *window,
124 const gchar *tny_folder_id);
126 static void cancel_progressbar (GtkToolButton *toolbutton,
127 ModestMsgViewWindow *self);
129 static void on_queue_changed (ModestMailOperationQueue *queue,
130 ModestMailOperation *mail_op,
131 ModestMailOperationQueueNotification type,
132 ModestMsgViewWindow *self);
134 static void on_account_removed (TnyAccountStore *account_store,
138 static void on_move_focus (GtkWidget *widget,
139 GtkDirectionType direction,
142 static void view_msg_cb (ModestMailOperation *mail_op,
149 static void set_toolbar_mode (ModestMsgViewWindow *self,
150 ModestToolBarModes mode);
152 static void update_window_title (ModestMsgViewWindow *window);
154 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
155 static void init_window (ModestMsgViewWindow *obj);
157 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
160 /* list my signals */
166 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
167 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
168 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
171 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
172 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
173 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
174 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
175 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
176 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
177 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
180 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
181 struct _ModestMsgViewWindowPrivate {
184 GtkWidget *main_scroll;
185 GtkWidget *find_toolbar;
188 /* Progress observers */
189 GtkWidget *progress_bar;
190 GSList *progress_widgets;
193 GtkWidget *progress_toolitem;
194 GtkWidget *cancel_toolitem;
195 GtkWidget *prev_toolitem;
196 GtkWidget *next_toolitem;
197 ModestToolBarModes current_toolbar_mode;
199 /* Optimized view enabled */
200 gboolean optimized_view;
202 /* Whether this was created via the *_new_for_search_result() function. */
203 gboolean is_search_result;
205 /* Whether the message is in outbox */
208 /* A reference to the @model of the header view
209 * to allow selecting previous/next messages,
210 * if the message is currently selected in the header view.
212 const gchar *header_folder_id;
213 GtkTreeModel *header_model;
214 GtkTreeRowReference *row_reference;
215 GtkTreeRowReference *next_row_reference;
217 gulong clipboard_change_handler;
218 gulong queue_change_handler;
219 gulong account_removed_handler;
220 gulong row_changed_handler;
221 gulong row_deleted_handler;
222 gulong row_inserted_handler;
223 gulong rows_reordered_handler;
226 GtkWidget *remove_attachment_banner;
228 guint progress_bar_timeout;
235 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
236 MODEST_TYPE_MSG_VIEW_WINDOW, \
237 ModestMsgViewWindowPrivate))
239 static GtkWindowClass *parent_class = NULL;
241 /* uncomment the following if you have defined any signals */
242 static guint signals[LAST_SIGNAL] = {0};
245 modest_msg_view_window_get_type (void)
247 static GType my_type = 0;
249 static const GTypeInfo my_info = {
250 sizeof(ModestMsgViewWindowClass),
251 NULL, /* base init */
252 NULL, /* base finalize */
253 (GClassInitFunc) modest_msg_view_window_class_init,
254 NULL, /* class finalize */
255 NULL, /* class data */
256 sizeof(ModestMsgViewWindow),
258 (GInstanceInitFunc) modest_msg_view_window_init,
261 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
262 "ModestMsgViewWindow",
265 static const GInterfaceInfo modest_header_view_observer_info =
267 (GInterfaceInitFunc) modest_header_view_observer_init,
268 NULL, /* interface_finalize */
269 NULL /* interface_data */
272 g_type_add_interface_static (my_type,
273 MODEST_TYPE_HEADER_VIEW_OBSERVER,
274 &modest_header_view_observer_info);
280 save_state (ModestWindow *self)
282 modest_widget_memory_save (modest_runtime_get_conf (),
284 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
289 restore_settings (ModestMsgViewWindow *self)
292 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
295 conf = modest_runtime_get_conf ();
296 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
297 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
298 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
299 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
300 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
301 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
302 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
303 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
304 modest_widget_memory_restore (conf,
306 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
310 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
312 GObjectClass *gobject_class;
313 ModestWindowClass *modest_window_class;
314 gobject_class = (GObjectClass*) klass;
315 modest_window_class = (ModestWindowClass *) klass;
317 parent_class = g_type_class_peek_parent (klass);
318 gobject_class->finalize = modest_msg_view_window_finalize;
320 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
321 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
322 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
323 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
324 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
325 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
327 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
329 modest_window_class->save_state_func = save_state;
331 signals[MSG_CHANGED_SIGNAL] =
332 g_signal_new ("msg-changed",
333 G_TYPE_FROM_CLASS (gobject_class),
335 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
337 modest_marshal_VOID__POINTER_POINTER,
338 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
341 static void modest_header_view_observer_init(
342 ModestHeaderViewObserverIface *iface_class)
344 iface_class->update_func = modest_msg_view_window_update_model_replaced;
348 modest_msg_view_window_init (ModestMsgViewWindow *obj)
350 ModestMsgViewWindowPrivate *priv;
351 ModestWindowPrivate *parent_priv = NULL;
352 GtkActionGroup *action_group = NULL;
353 GError *error = NULL;
354 GdkPixbuf *window_icon;
356 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
357 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
358 parent_priv->ui_manager = gtk_ui_manager_new();
360 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
361 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
363 /* Add common actions */
364 gtk_action_group_add_actions (action_group,
365 modest_action_entries,
366 G_N_ELEMENTS (modest_action_entries),
368 gtk_action_group_add_toggle_actions (action_group,
369 modest_toggle_action_entries,
370 G_N_ELEMENTS (modest_toggle_action_entries),
372 gtk_action_group_add_toggle_actions (action_group,
373 msg_view_toggle_action_entries,
374 G_N_ELEMENTS (msg_view_toggle_action_entries),
376 gtk_action_group_add_radio_actions (action_group,
377 msg_view_zoom_action_entries,
378 G_N_ELEMENTS (msg_view_zoom_action_entries),
380 G_CALLBACK (modest_ui_actions_on_change_zoom),
383 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
384 g_object_unref (action_group);
386 /* Load the UI definition */
387 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
390 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
391 g_error_free (error);
396 /* Add accelerators */
397 gtk_window_add_accel_group (GTK_WINDOW (obj),
398 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
400 priv->is_search_result = FALSE;
401 priv->is_outbox = FALSE;
403 priv->msg_view = NULL;
404 priv->header_model = NULL;
405 priv->header_folder_id = NULL;
406 priv->clipboard_change_handler = 0;
407 priv->queue_change_handler = 0;
408 priv->account_removed_handler = 0;
409 priv->row_changed_handler = 0;
410 priv->row_deleted_handler = 0;
411 priv->row_inserted_handler = 0;
412 priv->rows_reordered_handler = 0;
413 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
415 priv->optimized_view = FALSE;
416 priv->progress_bar_timeout = 0;
417 priv->purge_timeout = 0;
418 priv->remove_attachment_banner = NULL;
419 priv->msg_uid = NULL;
421 priv->sighandlers = NULL;
424 init_window (MODEST_MSG_VIEW_WINDOW(obj));
426 /* Set window icon */
427 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
429 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
430 g_object_unref (window_icon);
433 hildon_program_add_window (hildon_program_get_instance(),
436 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
437 GTK_WINDOW(obj),"applications_email_viewer");
442 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
444 ModestMsgViewWindowPrivate *priv = NULL;
446 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
448 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
450 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
452 if (priv->progress_bar_timeout > 0) {
453 g_source_remove (priv->progress_bar_timeout);
454 priv->progress_bar_timeout = 0;
461 set_toolbar_mode (ModestMsgViewWindow *self,
462 ModestToolBarModes mode)
464 ModestWindowPrivate *parent_priv;
465 ModestMsgViewWindowPrivate *priv;
466 /* GtkWidget *widget = NULL; */
468 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
470 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
473 /* Sets current toolbar mode */
474 priv->current_toolbar_mode = mode;
476 /* Update toolbar dimming state */
477 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
480 case TOOLBAR_MODE_NORMAL:
481 if (priv->progress_toolitem) {
482 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
483 gtk_widget_hide (priv->progress_toolitem);
486 if (priv->progress_bar)
487 gtk_widget_hide (priv->progress_bar);
489 if (priv->cancel_toolitem)
490 gtk_widget_hide (priv->cancel_toolitem);
492 if (priv->prev_toolitem)
493 gtk_widget_show (priv->prev_toolitem);
495 if (priv->next_toolitem)
496 gtk_widget_show (priv->next_toolitem);
498 /* Hide toolbar if optimized view is enabled */
499 if (priv->optimized_view) {
500 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
501 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
505 case TOOLBAR_MODE_TRANSFER:
506 if (priv->prev_toolitem)
507 gtk_widget_hide (priv->prev_toolitem);
509 if (priv->next_toolitem)
510 gtk_widget_hide (priv->next_toolitem);
512 if (priv->progress_bar)
513 gtk_widget_show (priv->progress_bar);
515 if (priv->progress_toolitem) {
516 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
517 gtk_widget_show (priv->progress_toolitem);
520 if (priv->cancel_toolitem)
521 gtk_widget_show (priv->cancel_toolitem);
523 /* Show toolbar if it's hiden (optimized view ) */
524 if (priv->optimized_view) {
525 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
526 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
531 g_return_if_reached ();
538 init_window (ModestMsgViewWindow *obj)
540 GtkWidget *main_vbox;
541 ModestMsgViewWindowPrivate *priv;
542 ModestWindowPrivate *parent_priv;
544 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
545 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
547 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
548 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
549 main_vbox = gtk_vbox_new (FALSE, 6);
551 #ifdef MODEST_USE_MOZEMBED
552 priv->main_scroll = priv->msg_view;
553 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
555 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
556 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
558 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
559 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
560 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
562 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
563 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
565 priv->find_toolbar = hildon_find_toolbar_new (NULL);
566 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
567 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
569 gtk_widget_show_all (GTK_WIDGET(main_vbox));
573 modest_msg_view_window_disconnect_signals (ModestWindow *self)
575 ModestMsgViewWindowPrivate *priv;
576 ModestHeaderView *header_view = NULL;
577 ModestWindow *main_window = NULL;
579 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
581 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
582 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
583 priv->clipboard_change_handler))
584 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
585 priv->clipboard_change_handler);
587 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
588 priv->queue_change_handler))
589 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
590 priv->queue_change_handler);
592 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
593 priv->account_removed_handler))
594 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
595 priv->account_removed_handler);
597 if (priv->header_model) {
598 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
599 priv->row_changed_handler))
600 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
601 priv->row_changed_handler);
603 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
604 priv->row_deleted_handler))
605 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
606 priv->row_deleted_handler);
608 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
609 priv->row_inserted_handler))
610 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
611 priv->row_inserted_handler);
613 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
614 priv->rows_reordered_handler))
615 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
616 priv->rows_reordered_handler);
619 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
620 priv->sighandlers = NULL;
622 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
623 FALSE); /* don't create */
627 header_view = MODEST_HEADER_VIEW(
628 modest_main_window_get_child_widget(
629 MODEST_MAIN_WINDOW(main_window),
630 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
631 if (header_view == NULL)
634 modest_header_view_remove_observer(header_view,
635 MODEST_HEADER_VIEW_OBSERVER(self));
639 modest_msg_view_window_finalize (GObject *obj)
641 ModestMsgViewWindowPrivate *priv;
643 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
645 /* Sanity check: shouldn't be needed, the window mgr should
646 call this function before */
647 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
649 if (priv->header_model != NULL) {
650 g_object_unref (priv->header_model);
651 priv->header_model = NULL;
654 if (priv->progress_bar_timeout > 0) {
655 g_source_remove (priv->progress_bar_timeout);
656 priv->progress_bar_timeout = 0;
659 if (priv->remove_attachment_banner) {
660 gtk_widget_destroy (priv->remove_attachment_banner);
661 g_object_unref (priv->remove_attachment_banner);
662 priv->remove_attachment_banner = NULL;
665 if (priv->purge_timeout > 0) {
666 g_source_remove (priv->purge_timeout);
667 priv->purge_timeout = 0;
670 if (priv->row_reference) {
671 gtk_tree_row_reference_free (priv->row_reference);
672 priv->row_reference = NULL;
675 if (priv->next_row_reference) {
676 gtk_tree_row_reference_free (priv->next_row_reference);
677 priv->next_row_reference = NULL;
681 g_free (priv->msg_uid);
682 priv->msg_uid = NULL;
685 G_OBJECT_CLASS(parent_class)->finalize (obj);
689 select_next_valid_row (GtkTreeModel *model,
690 GtkTreeRowReference **row_reference,
693 GtkTreeIter tmp_iter;
694 GtkTreePath *path, *next;
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);
724 /* TODO: This should be in _init(), with the parameters as properties. */
726 modest_msg_view_window_construct (ModestMsgViewWindow *self,
727 const gchar *modest_account_name,
728 const gchar *msg_uid)
731 ModestMsgViewWindowPrivate *priv = NULL;
732 ModestWindowPrivate *parent_priv = NULL;
733 ModestDimmingRulesGroup *menu_rules_group = NULL;
734 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
735 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
737 obj = G_OBJECT (self);
738 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
739 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
741 priv->msg_uid = g_strdup (msg_uid);
744 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
745 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
746 gtk_widget_show (parent_priv->menubar);
747 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
749 menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
750 toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
751 clipboard_rules_group = modest_dimming_rules_group_new ("ModestClipboardDimmingRules", FALSE);
753 /* Add common dimming rules */
754 modest_dimming_rules_group_add_rules (menu_rules_group,
755 modest_msg_view_menu_dimming_entries,
756 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
757 MODEST_WINDOW (self));
758 modest_dimming_rules_group_add_rules (toolbar_rules_group,
759 modest_msg_view_toolbar_dimming_entries,
760 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
761 MODEST_WINDOW (self));
762 modest_dimming_rules_group_add_rules (clipboard_rules_group,
763 modest_msg_view_clipboard_dimming_entries,
764 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
765 MODEST_WINDOW (self));
767 /* Insert dimming rules group for this window */
768 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
769 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
770 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
771 g_object_unref (menu_rules_group);
772 g_object_unref (toolbar_rules_group);
773 g_object_unref (clipboard_rules_group);
775 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
777 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
779 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);
780 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
781 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
782 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
783 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
784 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
785 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
786 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
787 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
788 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
789 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
791 g_signal_connect (G_OBJECT (obj), "key-release-event",
792 G_CALLBACK (modest_msg_view_window_key_event),
795 g_signal_connect (G_OBJECT (obj), "key-press-event",
796 G_CALLBACK (modest_msg_view_window_key_event),
799 g_signal_connect (G_OBJECT (obj), "window-state-event",
800 G_CALLBACK (modest_msg_view_window_window_state_event),
803 g_signal_connect (G_OBJECT (obj), "move-focus",
804 G_CALLBACK (on_move_focus), obj);
806 /* Mail Operation Queue */
807 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
809 G_CALLBACK (on_queue_changed),
812 /* Account manager */
813 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
815 G_CALLBACK(on_account_removed),
818 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
820 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
821 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
822 priv->last_search = NULL;
824 /* Init the clipboard actions dim status */
825 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
827 gtk_widget_show_all (GTK_WIDGET (obj));
828 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
833 /* FIXME: parameter checks */
835 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
836 const gchar *modest_account_name,
837 const gchar *msg_uid,
839 GtkTreeRowReference *row_reference)
841 ModestMsgViewWindow *window = NULL;
842 ModestMsgViewWindowPrivate *priv = NULL;
843 TnyFolder *header_folder = NULL;
844 ModestHeaderView *header_view = NULL;
845 ModestWindow *main_window = NULL;
846 ModestWindowMgr *mgr = NULL;
848 mgr = modest_runtime_get_window_mgr ();
849 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
850 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
852 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
854 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
856 /* Remember the message list's TreeModel so we can detect changes
857 * and change the list selection when necessary: */
859 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
861 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
862 MODEST_MAIN_WINDOW(main_window),
863 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
866 if (header_view != NULL){
867 header_folder = modest_header_view_get_folder(header_view);
868 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
869 g_assert(header_folder != NULL);
870 priv->header_folder_id = tny_folder_get_id(header_folder);
871 g_assert(priv->header_folder_id != NULL);
872 g_object_unref(header_folder);
875 priv->header_model = g_object_ref(model);
877 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
878 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
879 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
881 priv->row_reference = NULL;
882 priv->next_row_reference = NULL;
885 priv->row_changed_handler = g_signal_connect(
886 GTK_TREE_MODEL(model), "row-changed",
887 G_CALLBACK(modest_msg_view_window_on_row_changed),
889 priv->row_deleted_handler = g_signal_connect(
890 GTK_TREE_MODEL(model), "row-deleted",
891 G_CALLBACK(modest_msg_view_window_on_row_deleted),
893 priv->row_inserted_handler = g_signal_connect (
894 GTK_TREE_MODEL(model), "row-inserted",
895 G_CALLBACK(modest_msg_view_window_on_row_inserted),
897 priv->rows_reordered_handler = g_signal_connect(
898 GTK_TREE_MODEL(model), "rows-reordered",
899 G_CALLBACK(modest_msg_view_window_on_row_reordered),
902 if (header_view != NULL){
903 modest_header_view_add_observer(header_view,
904 MODEST_HEADER_VIEW_OBSERVER(window));
907 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
908 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
909 gtk_widget_show_all (GTK_WIDGET (window));
910 modest_msg_view_window_update_priority (window);
912 /* Check toolbar dimming rules */
913 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
914 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
916 return MODEST_WINDOW(window);
920 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
921 const gchar *modest_account_name,
922 const gchar *msg_uid)
924 ModestMsgViewWindow *window = NULL;
925 ModestMsgViewWindowPrivate *priv = NULL;
926 ModestWindowMgr *mgr = NULL;
928 mgr = modest_runtime_get_window_mgr ();
929 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
930 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
931 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
933 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
935 /* Remember that this is a search result,
936 * so we can disable some UI appropriately: */
937 priv->is_search_result = TRUE;
939 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
941 update_window_title (window);
942 modest_msg_view_window_update_priority (window);
945 return MODEST_WINDOW(window);
949 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
950 const gchar *modest_account_name,
951 const gchar *msg_uid)
954 ModestMsgViewWindowPrivate *priv;
955 ModestWindowMgr *mgr = NULL;
957 g_return_val_if_fail (msg, NULL);
958 mgr = modest_runtime_get_window_mgr ();
959 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
960 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
961 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
962 modest_account_name, msg_uid);
964 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
965 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
967 /* Check toolbar dimming rules */
968 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
969 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), "ModestClipboardDimmingRules");
971 return MODEST_WINDOW(obj);
974 void modest_msg_view_window_on_row_changed(
975 GtkTreeModel *header_model,
978 ModestMsgViewWindow *window){
979 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
982 void modest_msg_view_window_on_row_deleted(
983 GtkTreeModel *header_model,
985 ModestMsgViewWindow *window){
986 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
989 /* On insertions we check if the folder still has the message we are
990 * showing or do not. If do not, we do nothing. Which means we are still
991 * not attached to any header folder and thus next/prev buttons are
992 * still dimmed. Once the message that is shown by msg-view is found, the
993 * new model of header-view will be attached and the references will be set.
994 * On each further insertions dimming rules will be checked. However
995 * this requires extra CPU time at least works.
996 * (An message might be deleted from TnyFolder and thus will not be
997 * inserted into the model again for example if it is removed by the
998 * imap server and the header view is refreshed.)
1000 void modest_msg_view_window_on_row_inserted(
1001 GtkTreeModel *new_model,
1002 GtkTreePath *tree_path,
1003 GtkTreeIter *tree_iter,
1004 ModestMsgViewWindow *window){
1005 ModestMsgViewWindowPrivate *priv = NULL;
1006 TnyHeader *header = NULL;
1009 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1011 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1013 /* If we already has a model attached then the message shown by
1014 * msg-view is in it, and thus we do not need any actions but
1015 * to check the dimming rules.*/
1016 if(priv->header_model != NULL){
1017 if (priv->row_reference) {
1018 gtk_tree_row_reference_free(priv->next_row_reference);
1019 priv->next_row_reference = gtk_tree_row_reference_copy(
1020 priv->row_reference);
1021 select_next_valid_row (priv->header_model,
1022 &(priv->next_row_reference), FALSE);
1024 modest_ui_actions_check_toolbar_dimming_rules (
1025 MODEST_WINDOW (window));
1029 /* Check if the newly inserted message is the same we are actually
1030 * showing. IF not, we should remain detached from the header model
1031 * and thus prev and next toolbarbuttons should remain dimmed. */
1032 gtk_tree_model_get (new_model, tree_iter,
1033 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1034 uid = modest_tny_folder_get_header_unique_id(header);
1035 if(!g_str_equal(priv->msg_uid, uid)){
1037 g_object_unref(G_OBJECT(header));
1041 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1042 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1043 g_object_unref(G_OBJECT(header));
1047 /* Setup row_reference for the actual msg. */
1048 priv->row_reference = gtk_tree_row_reference_new(
1049 new_model, tree_path);
1050 if(priv->row_reference == NULL){
1051 g_warning("No reference for msg header item.");
1055 /* Attach new_model and connect some callback to it to become able
1056 * to detect changes in header-view. */
1057 priv->header_model = g_object_ref(new_model);
1058 g_signal_connect (new_model, "row-changed",
1059 G_CALLBACK (modest_msg_view_window_on_row_changed),
1061 g_signal_connect (new_model, "row-deleted",
1062 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1064 g_signal_connect (new_model, "rows-reordered",
1065 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1068 /* Now set up next_row_reference. */
1069 priv->next_row_reference = gtk_tree_row_reference_copy(
1070 priv->row_reference);
1071 select_next_valid_row (priv->header_model,
1072 &(priv->next_row_reference), FALSE);
1074 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1077 void modest_msg_view_window_on_row_reordered(
1078 GtkTreeModel *header_model,
1082 ModestMsgViewWindow *window){
1083 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1086 /* The modest_msg_view_window_update_model_replaced implements update
1087 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1088 * actually belongs to the header-view is the same as the TnyFolder of
1089 * the message of msg-view or not. If they are different, there is
1090 * nothing to do. If they are the same, then the model has replaced and
1091 * the reference in msg-view shall be replaced from the old model to
1092 * the new model. In this case the view will be detached from it's
1093 * header folder. From this point the next/prev buttons are dimmed.
1095 void modest_msg_view_window_update_model_replaced(
1096 ModestHeaderViewObserver *observer,
1097 GtkTreeModel *model,
1098 const gchar *tny_folder_id){
1099 ModestMsgViewWindowPrivate *priv = NULL;
1100 ModestMsgViewWindow *window = NULL;
1102 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1103 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1105 window = MODEST_MSG_VIEW_WINDOW(observer);
1106 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1108 /* If there is an other folder in the header-view then we do
1109 * not care about it's model (msg list). Else if the
1110 * header-view shows the folder the msg shown by us is in, we
1111 * shall replace our model reference and make some check. */
1112 if(tny_folder_id == NULL || !g_str_equal(tny_folder_id, priv->header_folder_id))
1115 /* Model is changed(replaced), so we should forget the old
1116 * one. Because there might be other references and there
1117 * might be some change on the model even if we unreferenced
1118 * it, we need to disconnect our signals here. */
1119 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1120 priv->row_changed_handler))
1121 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1122 priv->row_changed_handler);
1123 priv->row_changed_handler = 0;
1124 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1125 priv->row_deleted_handler))
1126 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1127 priv->row_deleted_handler);
1128 priv->row_deleted_handler = 0;
1129 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1130 priv->row_inserted_handler))
1131 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1132 priv->row_inserted_handler);
1133 priv->row_inserted_handler = 0;
1134 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1135 priv->rows_reordered_handler))
1136 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1137 priv->rows_reordered_handler);
1138 priv->rows_reordered_handler = 0;
1139 g_object_unref(priv->header_model);
1140 priv->header_model = NULL;
1141 if (priv->row_reference)
1142 g_object_unref(priv->row_reference);
1143 priv->row_reference = NULL;
1144 if (priv->next_row_reference)
1145 g_object_unref(priv->next_row_reference);
1146 priv->next_row_reference = NULL;
1148 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1150 g_assert(model != NULL);
1152 /* Also we must connect to the new model for row insertions.
1153 * Only for insertions now. We will need other ones only after
1154 * the msg is show by msg-view is added to the new model. */
1155 priv->row_inserted_handler = g_signal_connect (
1156 model, "row-inserted",
1157 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1162 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1164 ModestMsgViewWindowPrivate *priv= NULL;
1166 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1167 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1169 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1173 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1175 ModestMsgViewWindowPrivate *priv= NULL;
1177 TnyHeader *header = NULL;
1178 GtkTreePath *path = NULL;
1181 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1182 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1184 /* If the message was not obtained from a treemodel,
1185 * for instance if it was opened directly by the search UI:
1187 if (priv->header_model == NULL || priv->row_reference == NULL) {
1188 msg = modest_msg_view_window_get_message (self);
1190 header = tny_msg_get_header (msg);
1191 g_object_unref (msg);
1196 /* Get iter of the currently selected message in the header view: */
1197 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1198 * instead of sometimes retrieving it from the header view?
1199 * Then we wouldn't be dependent on the message actually still being selected
1200 * in the header view. murrayc. */
1201 if (!gtk_tree_row_reference_valid (priv->row_reference))
1203 path = gtk_tree_row_reference_get_path (priv->row_reference);
1204 g_return_val_if_fail (path != NULL, NULL);
1205 gtk_tree_model_get_iter (priv->header_model,
1209 /* Get current message header */
1210 gtk_tree_model_get (priv->header_model, &iter,
1211 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1214 gtk_tree_path_free (path);
1219 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1221 ModestMsgViewWindowPrivate *priv;
1223 g_return_val_if_fail (self, NULL);
1225 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1227 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1231 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1233 ModestMsgViewWindowPrivate *priv;
1235 g_return_val_if_fail (self, NULL);
1237 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1239 return (const gchar*) priv->msg_uid;
1243 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1246 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1247 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1248 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1252 is_active = gtk_toggle_action_get_active (toggle);
1255 gtk_widget_show (priv->find_toolbar);
1256 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1258 gtk_widget_hide (priv->find_toolbar);
1261 /* update the toggle buttons status */
1262 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1263 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1264 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1265 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1270 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1271 ModestMsgViewWindow *obj)
1273 GtkToggleAction *toggle;
1274 ModestWindowPrivate *parent_priv;
1275 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1277 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1278 gtk_toggle_action_set_active (toggle, FALSE);
1282 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1283 ModestMsgViewWindow *obj)
1285 gchar *current_search;
1286 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1288 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1289 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1293 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1295 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1296 g_free (current_search);
1297 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1301 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1303 g_free (priv->last_search);
1304 priv->last_search = g_strdup (current_search);
1305 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1308 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1309 g_free (priv->last_search);
1310 priv->last_search = NULL;
1312 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1315 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1316 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1317 g_free (priv->last_search);
1318 priv->last_search = NULL;
1320 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1324 g_free (current_search);
1329 modest_msg_view_window_set_zoom (ModestWindow *window,
1332 ModestMsgViewWindowPrivate *priv;
1333 ModestWindowPrivate *parent_priv;
1334 GtkAction *action = NULL;
1335 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1337 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1339 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1340 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1341 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1343 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1344 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1346 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1350 modest_msg_view_window_get_zoom (ModestWindow *window)
1352 ModestMsgViewWindowPrivate *priv;
1354 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1356 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1357 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1361 modest_msg_view_window_zoom_plus (ModestWindow *window)
1363 ModestWindowPrivate *parent_priv;
1364 GtkRadioAction *zoom_radio_action;
1365 GSList *group, *node;
1367 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1368 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1369 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1371 group = gtk_radio_action_get_group (zoom_radio_action);
1373 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1374 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1378 for (node = group; node != NULL; node = g_slist_next (node)) {
1379 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1380 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1388 modest_msg_view_window_zoom_minus (ModestWindow *window)
1390 ModestWindowPrivate *parent_priv;
1391 GtkRadioAction *zoom_radio_action;
1392 GSList *group, *node;
1394 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1395 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1396 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1398 group = gtk_radio_action_get_group (zoom_radio_action);
1400 for (node = group; node != NULL; node = g_slist_next (node)) {
1401 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1402 if (node->next != NULL) {
1403 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1406 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1416 modest_msg_view_window_key_event (GtkWidget *window,
1421 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1422 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1423 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1424 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1425 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1426 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1427 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1428 gboolean return_value;
1430 if (event->type == GDK_KEY_RELEASE) {
1431 GtkScrollType scroll_type;
1433 switch (event->keyval) {
1436 scroll_type = GTK_SCROLL_STEP_UP; break;
1439 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1441 case GDK_KP_Page_Up:
1442 scroll_type = GTK_SCROLL_PAGE_UP; break;
1444 case GDK_KP_Page_Down:
1445 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1448 scroll_type = GTK_SCROLL_START; break;
1451 scroll_type = GTK_SCROLL_END; break;
1452 default: scroll_type = GTK_SCROLL_NONE;
1455 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",
1456 scroll_type, FALSE, &return_value);
1467 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1470 ModestMsgViewWindowPrivate *priv;
1471 GtkTreeIter tmp_iter;
1472 gboolean is_last_selected;
1474 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1475 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1477 /*if no model (so no rows at all), then virtually we are the last*/
1478 if (!priv->header_model || !priv->row_reference)
1481 path = gtk_tree_row_reference_get_path (priv->row_reference);
1485 is_last_selected = TRUE;
1486 while (is_last_selected) {
1488 gtk_tree_path_next (path);
1489 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1491 gtk_tree_model_get (priv->header_model, &tmp_iter,
1492 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1495 if (msg_is_visible (header, priv->is_outbox))
1496 is_last_selected = FALSE;
1497 g_object_unref(G_OBJECT(header));
1500 gtk_tree_path_free (path);
1501 return is_last_selected;
1505 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1507 ModestMsgViewWindowPrivate *priv;
1509 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1510 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1512 return priv->header_model != NULL;
1516 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1518 ModestMsgViewWindowPrivate *priv;
1520 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1521 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1523 return priv->is_search_result;
1527 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1529 return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1530 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1535 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1538 ModestMsgViewWindowPrivate *priv;
1539 gboolean is_first_selected;
1540 GtkTreeIter tmp_iter;
1541 /* gchar * path_string;*/
1543 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1544 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1546 /*if no model (so no rows at all), then virtually we are the first*/
1547 if (!priv->header_model || !priv->row_reference)
1550 path = gtk_tree_row_reference_get_path (priv->row_reference);
1554 /* path_string = gtk_tree_path_to_string (path);
1555 is_first_selected = strcmp (path_string, "0");
1557 g_free (path_string);
1558 gtk_tree_path_free (path);
1560 return is_first_selected;*/
1562 is_first_selected = TRUE;
1563 while (is_first_selected) {
1565 if(!gtk_tree_path_prev (path))
1567 /* Here the 'if' is needless for logic, but let make sure
1568 * iter is valid for gtk_tree_model_get. */
1569 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1571 gtk_tree_model_get (priv->header_model, &tmp_iter,
1572 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1575 if (msg_is_visible (header, priv->is_outbox))
1576 is_first_selected = FALSE;
1577 g_object_unref(G_OBJECT(header));
1580 gtk_tree_path_free (path);
1581 return is_first_selected;
1585 * Reads the message whose summary item is @header. It takes care of
1586 * several things, among others:
1588 * If the message was not previously downloaded then ask the user
1589 * before downloading. If there is no connection launch the connection
1590 * dialog. Update toolbar dimming rules.
1592 * Returns: TRUE if the mail operation was started, otherwise if the
1593 * user do not want to download the message, or if the user do not
1594 * want to connect, then the operation is not issued
1597 message_reader (ModestMsgViewWindow *window,
1598 ModestMsgViewWindowPrivate *priv,
1600 GtkTreeRowReference *row_reference)
1602 ModestMailOperation *mail_op = NULL;
1603 gboolean already_showing = FALSE;
1604 ModestWindow *msg_window = NULL;
1605 ModestWindowMgr *mgr;
1607 g_return_val_if_fail (row_reference != NULL, FALSE);
1609 mgr = modest_runtime_get_window_mgr ();
1610 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1611 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1614 gtk_window_present (GTK_WINDOW (msg_window));
1615 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1619 /* Msg download completed */
1620 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1621 /* Ask the user if he wants to download the message if
1623 if (!tny_device_is_online (modest_runtime_get_device())) {
1624 TnyFolder *folder = NULL;
1625 GtkResponseType response;
1627 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1628 _("mcen_nc_get_msg"));
1629 if (response == GTK_RESPONSE_CANCEL)
1632 /* Offer the connection dialog if necessary */
1633 folder = tny_header_get_folder (header);
1635 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1636 TNY_FOLDER_STORE (folder))) {
1637 g_object_unref (folder);
1640 g_object_unref (folder);
1645 /* New mail operation */
1646 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(window),
1647 modest_ui_actions_get_msgs_full_error_handler,
1650 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1651 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, row_reference);
1652 g_object_unref (mail_op);
1654 /* Update toolbar dimming rules */
1655 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1661 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1663 ModestMsgViewWindowPrivate *priv;
1664 GtkTreePath *path= NULL;
1665 GtkTreeIter tmp_iter;
1667 gboolean retval = TRUE;
1668 GtkTreeRowReference *row_reference = NULL;
1670 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1671 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1673 if (!priv->row_reference)
1676 /* Update the next row reference if it's not valid. This could
1677 happen if for example the header which it was pointing to,
1678 was deleted. The best place to do it is in the row-deleted
1679 handler but the tinymail model do not work like the glib
1680 tree models and reports the deletion when the row is still
1682 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1683 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1684 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1685 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1688 if (priv->next_row_reference)
1689 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1693 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1695 gtk_tree_model_get_iter (priv->header_model,
1698 gtk_tree_path_free (path);
1700 gtk_tree_model_get (priv->header_model, &tmp_iter,
1701 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1704 /* Read the message & show it */
1705 if (!message_reader (window, priv, header, row_reference)) {
1707 gtk_tree_row_reference_free (row_reference);
1711 g_object_unref (header);
1717 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1719 ModestMsgViewWindowPrivate *priv = NULL;
1720 TnyHeader *header = NULL;
1722 GtkTreePath *path = NULL;
1723 GtkTreeRowReference *row_reference = NULL;
1725 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1726 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1728 /* Check that the model is not empty */
1729 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1732 /* Get the header */
1733 gtk_tree_model_get (priv->header_model,
1735 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1737 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1738 if (!msg_is_visible (header, priv->is_outbox)) {
1739 g_object_unref (header);
1740 return modest_msg_view_window_select_next_message (self);
1743 path = gtk_tree_model_get_path (priv->header_model, &iter);
1744 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1745 gtk_tree_path_free (path);
1747 /* Read the message & show it */
1748 message_reader (self, priv, header, row_reference);
1751 g_object_unref (header);
1757 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1759 ModestMsgViewWindowPrivate *priv = NULL;
1761 GtkTreeRowReference *row_reference = NULL;
1763 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1764 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1766 /* Return inmediatly if there is no header model */
1767 if (!priv->header_model || !priv->row_reference)
1770 path = gtk_tree_row_reference_get_path (priv->row_reference);
1771 while (gtk_tree_path_prev (path)) {
1775 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1776 gtk_tree_model_get (priv->header_model, &iter,
1777 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1781 if (!msg_is_visible (header, priv->is_outbox)) {
1782 g_object_unref (header);
1786 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1787 /* Read the message & show it */
1788 if (!message_reader (window, priv, header, row_reference)) {
1789 gtk_tree_row_reference_free (row_reference);
1790 g_object_unref (header);
1794 gtk_tree_path_free (path);
1795 g_object_unref (header);
1800 gtk_tree_path_free (path);
1805 view_msg_cb (ModestMailOperation *mail_op,
1812 ModestMsgViewWindow *self = NULL;
1813 ModestMsgViewWindowPrivate *priv = NULL;
1814 GtkTreeRowReference *row_reference = NULL;
1817 g_object_unref (self);
1821 /* If there was any error */
1822 row_reference = (GtkTreeRowReference *) user_data;
1823 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1824 gtk_tree_row_reference_free (row_reference);
1828 /* Get the window */
1829 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1830 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1831 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1833 /* Update the row reference */
1834 if (priv->row_reference != NULL) {
1835 gtk_tree_row_reference_free (priv->row_reference);
1836 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1837 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1838 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1839 gtk_tree_row_reference_free (row_reference);
1842 /* Mark header as read */
1843 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1844 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1846 /* Set new message */
1847 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1848 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1849 modest_msg_view_window_update_priority (self);
1850 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1851 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1854 /* Set the new message uid of the window */
1855 if (priv->msg_uid) {
1856 g_free (priv->msg_uid);
1857 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1860 /* Notify the observers */
1861 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1862 0, priv->header_model, priv->row_reference);
1864 /* Free new references */
1865 g_object_unref (self);
1869 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1871 ModestMsgViewWindowPrivate *priv;
1873 TnyFolderType folder_type;
1875 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1877 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1879 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1883 folder = tny_msg_get_folder (msg);
1885 folder_type = tny_folder_get_folder_type (folder);
1886 g_object_unref (folder);
1888 g_object_unref (msg);
1896 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1898 ModestMsgViewWindowPrivate *priv;
1899 TnyHeader *header = NULL;
1900 TnyHeaderFlags flags = 0;
1902 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1904 if (priv->header_model && priv->row_reference) {
1906 GtkTreePath *path = NULL;
1908 path = gtk_tree_row_reference_get_path (priv->row_reference);
1909 g_return_if_fail (path != NULL);
1910 gtk_tree_model_get_iter (priv->header_model,
1912 gtk_tree_row_reference_get_path (priv->row_reference));
1914 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1916 gtk_tree_path_free (path);
1919 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1921 header = tny_msg_get_header (msg);
1922 g_object_unref (msg);
1927 flags = tny_header_get_flags (header);
1928 g_object_unref(G_OBJECT(header));
1931 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1936 toolbar_resize (ModestMsgViewWindow *self)
1938 ModestMsgViewWindowPrivate *priv = NULL;
1939 ModestWindowPrivate *parent_priv = NULL;
1941 gint static_button_size;
1942 ModestWindowMgr *mgr;
1944 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1945 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1946 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1948 mgr = modest_runtime_get_window_mgr ();
1949 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
1951 if (parent_priv->toolbar) {
1952 /* left size buttons */
1953 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
1954 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1955 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1956 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1957 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
1958 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1959 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1960 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1961 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
1962 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1963 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1964 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1965 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1966 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1967 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1968 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1970 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
1971 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1972 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1973 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1974 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1975 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1976 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1977 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1983 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1985 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1986 ModestWindowPrivate *parent_priv;
1987 ModestWindowMgr *mgr;
1988 gboolean is_fullscreen;
1989 GtkAction *fs_toggle_action;
1992 mgr = modest_runtime_get_window_mgr ();
1993 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1995 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1997 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1998 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1999 if (is_fullscreen != active) {
2000 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2002 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2010 modest_msg_view_window_show_toolbar (ModestWindow *self,
2011 gboolean show_toolbar)
2013 ModestMsgViewWindowPrivate *priv = NULL;
2014 ModestWindowPrivate *parent_priv;
2015 GtkWidget *reply_button = NULL, *menu = NULL;
2016 GtkWidget *placeholder = NULL;
2018 const gchar *action_name;
2021 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2022 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2024 /* Set optimized view status */
2025 priv->optimized_view = !show_toolbar;
2027 if (!parent_priv->toolbar) {
2028 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2030 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2032 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2033 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2034 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2035 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2036 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2038 /* Add ProgressBar (Transfer toolbar) */
2039 priv->progress_bar = modest_progress_bar_widget_new ();
2040 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2041 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2042 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2043 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2044 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2046 /* Connect cancel 'clicked' signal to abort progress mode */
2047 g_signal_connect(priv->cancel_toolitem, "clicked",
2048 G_CALLBACK(cancel_progressbar),
2051 /* Add it to the observers list */
2052 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2055 hildon_window_add_toolbar (HILDON_WINDOW (self),
2056 GTK_TOOLBAR (parent_priv->toolbar));
2058 /* Set reply button tap and hold menu */
2059 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2060 "/ToolBar/ToolbarMessageReply");
2061 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2062 "/ToolbarReplyCSM");
2063 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2067 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2068 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2069 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2071 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2072 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2073 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2075 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2078 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2079 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2082 /* Update also the actions (to update the toggles in the
2083 menus), we have to do it manually because some other window
2084 of the same time could have changed it (remember that the
2085 toolbar fullscreen mode is shared by all the windows of the
2087 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2088 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2090 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2092 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2093 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2098 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2100 ModestMsgViewWindow *window)
2102 if (!GTK_WIDGET_VISIBLE (window))
2105 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
2109 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2111 ModestMsgViewWindowPrivate *priv;
2113 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2114 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2116 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2120 cancel_progressbar (GtkToolButton *toolbutton,
2121 ModestMsgViewWindow *self)
2124 ModestMsgViewWindowPrivate *priv;
2126 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2128 /* Get operation observers and cancel its current operation */
2129 tmp = priv->progress_widgets;
2131 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2132 tmp=g_slist_next(tmp);
2136 observers_empty (ModestMsgViewWindow *self)
2139 ModestMsgViewWindowPrivate *priv;
2140 gboolean is_empty = TRUE;
2141 guint pending_ops = 0;
2143 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2144 tmp = priv->progress_widgets;
2146 /* Check all observers */
2147 while (tmp && is_empty) {
2148 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2149 is_empty = pending_ops == 0;
2151 tmp = g_slist_next(tmp);
2158 on_account_removed (TnyAccountStore *account_store,
2159 TnyAccount *account,
2162 /* Do nothing if it's a transport account, because we only
2163 show the messages of a store account */
2164 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2165 const gchar *parent_acc = NULL;
2166 const gchar *our_acc = NULL;
2168 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2169 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2171 /* Close this window if I'm showing a message of the removed account */
2172 if (strcmp (parent_acc, our_acc) == 0)
2173 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2178 on_mail_operation_started (ModestMailOperation *mail_op,
2181 ModestMsgViewWindow *self;
2182 ModestMailOperationTypeOperation op_type;
2184 ModestMsgViewWindowPrivate *priv;
2185 GObject *source = NULL;
2187 self = MODEST_MSG_VIEW_WINDOW (user_data);
2188 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2189 op_type = modest_mail_operation_get_type_operation (mail_op);
2190 tmp = priv->progress_widgets;
2191 source = modest_mail_operation_get_source(mail_op);
2192 if (G_OBJECT (self) == source) {
2193 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2194 set_toolbar_transfer_mode(self);
2196 modest_progress_object_add_operation (
2197 MODEST_PROGRESS_OBJECT (tmp->data),
2199 tmp = g_slist_next (tmp);
2203 g_object_unref (source);
2207 on_mail_operation_finished (ModestMailOperation *mail_op,
2210 ModestMsgViewWindow *self;
2211 ModestMailOperationTypeOperation op_type;
2213 ModestMsgViewWindowPrivate *priv;
2215 self = MODEST_MSG_VIEW_WINDOW (user_data);
2216 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2217 op_type = modest_mail_operation_get_type_operation (mail_op);
2218 tmp = priv->progress_widgets;
2220 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2222 modest_progress_object_remove_operation (
2223 MODEST_PROGRESS_OBJECT (tmp->data),
2225 tmp = g_slist_next (tmp);
2228 /* If no more operations are being observed, NORMAL mode is enabled again */
2229 if (observers_empty (self)) {
2230 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2236 on_queue_changed (ModestMailOperationQueue *queue,
2237 ModestMailOperation *mail_op,
2238 ModestMailOperationQueueNotification type,
2239 ModestMsgViewWindow *self)
2241 ModestMsgViewWindowPrivate *priv;
2243 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2245 /* If this operations was created by another window, do nothing */
2246 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2249 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2250 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2252 "operation-started",
2253 G_CALLBACK (on_mail_operation_started),
2255 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2257 "operation-finished",
2258 G_CALLBACK (on_mail_operation_finished),
2260 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2261 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2263 "operation-started");
2264 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2266 "operation-finished");
2271 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2273 ModestMsgViewWindowPrivate *priv;
2274 GList *selected_attachments = NULL;
2276 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2277 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2279 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2281 return selected_attachments;
2285 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2287 ModestMsgViewWindowPrivate *priv;
2288 const gchar *msg_uid;
2289 gchar *attachment_uid = NULL;
2290 gint attachment_index = 0;
2293 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2294 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2295 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2297 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2298 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2299 attachment_index = g_list_index (attachments, mime_part);
2300 g_list_free (attachments);
2302 if (msg_uid && attachment_index >= 0) {
2303 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2306 if (mime_part == NULL) {
2307 gboolean error = FALSE;
2308 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2309 if (selected_attachments == NULL) {
2311 } else if (g_list_length (selected_attachments) > 1) {
2312 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2315 mime_part = (TnyMimePart *) selected_attachments->data;
2316 g_object_ref (mime_part);
2318 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2319 g_list_free (selected_attachments);
2324 g_object_ref (mime_part);
2327 if (tny_mime_part_is_purged (mime_part)) {
2328 g_object_unref (mime_part);
2332 if (!TNY_IS_MSG (mime_part)) {
2333 gchar *filepath = NULL;
2334 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2335 const gchar *content_type;
2336 gboolean show_error_banner = FALSE;
2338 TnyFsStream *temp_stream = NULL;
2339 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2342 if (temp_stream != NULL) {
2343 content_type = tny_mime_part_get_content_type (mime_part);
2344 if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2345 /* make the file read-only */
2346 if (g_chmod(filepath, 0444) != 0)
2347 g_warning ("%s: failed to set file '%s' to read-only: %s",
2348 __FUNCTION__, filepath, strerror(errno));
2350 modest_platform_activate_file (filepath, content_type);
2352 /* error while saving attachment, maybe cerm_device_memory_full */
2353 show_error_banner = TRUE;
2355 g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2359 g_object_unref (temp_stream);
2361 /* NOTE: files in the temporary area will be automatically
2362 * cleaned after some time if they are no longer in use */
2364 if (filepath != NULL) {
2365 /* the file may already exist but it isn't writable,
2366 * let's try to open it anyway */
2367 content_type = tny_mime_part_get_content_type (mime_part);
2368 modest_platform_activate_file (filepath, content_type);
2371 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2372 show_error_banner = TRUE;
2375 if (show_error_banner)
2376 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2378 /* message attachment */
2379 TnyHeader *header = NULL;
2380 ModestWindowMgr *mgr;
2381 ModestWindow *msg_win = NULL;
2384 header = tny_msg_get_header (TNY_MSG (mime_part));
2385 mgr = modest_runtime_get_window_mgr ();
2386 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2389 if (msg_win) /* there is already a window for this uid; top it */
2390 gtk_window_present (GTK_WINDOW(msg_win));
2392 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2393 * thus, we don't do anything */
2394 g_warning ("window for is already being created");
2396 /* it's not found, so create a new window for it */
2397 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2398 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2400 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2401 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2402 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2403 modest_window_get_zoom (MODEST_WINDOW (window)));
2404 modest_window_mgr_register_window (mgr, msg_win);
2405 gtk_widget_show_all (GTK_WIDGET (msg_win));
2408 g_object_unref (mime_part);
2424 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2425 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2426 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2427 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2430 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2434 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2435 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2436 g_free (pair->filename);
2437 g_object_unref (pair->part);
2438 g_slice_free (SaveMimePartPair, pair);
2440 g_list_free (info->pairs);
2443 gtk_widget_destroy (info->banner);
2444 g_object_unref (info->banner);
2445 g_slice_free (SaveMimePartInfo, info);
2450 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2452 if (info->pairs != NULL) {
2453 save_mime_part_to_file (info);
2456 result = info->result;
2458 /* This is a GDK lock because we are an idle callback and
2459 * hildon_banner_show_information is or does Gtk+ code */
2461 gdk_threads_enter (); /* CHECKED */
2462 save_mime_part_info_free (info, TRUE);
2464 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2466 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2468 gdk_threads_leave (); /* CHECKED */
2475 save_mime_part_to_file (SaveMimePartInfo *info)
2477 GnomeVFSResult result;
2478 GnomeVFSHandle *handle;
2480 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2481 gboolean decode_result = TRUE;
2483 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2484 if (result == GNOME_VFS_OK) {
2485 stream = tny_vfs_stream_new (handle);
2486 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2487 decode_result = FALSE;
2489 g_object_unref (G_OBJECT (stream));
2490 g_object_unref (pair->part);
2491 g_slice_free (SaveMimePartPair, pair);
2492 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2493 info->result = decode_result;
2495 save_mime_part_info_free (info, FALSE);
2496 info->result = FALSE;
2499 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2504 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2506 gboolean is_ok = TRUE;
2507 gint replaced_files = 0;
2508 const GList *files = info->pairs;
2511 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2512 SaveMimePartPair *pair = iter->data;
2513 if (modest_utils_file_exists (pair->filename)) {
2517 if (replaced_files) {
2518 GtkWidget *confirm_overwrite_dialog;
2519 const gchar *message = (replaced_files == 1) ?
2520 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2521 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2522 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2525 gtk_widget_destroy (confirm_overwrite_dialog);
2529 save_mime_part_info_free (info, TRUE);
2531 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2532 _CS("sfil_ib_saving"));
2533 info->banner = g_object_ref (banner);
2534 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2535 g_object_unref (banner);
2542 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2544 gboolean clean_list = FALSE;
2545 ModestMsgViewWindowPrivate *priv;
2546 GList *files_to_save = NULL;
2547 GtkWidget *save_dialog = NULL;
2548 gchar *folder = NULL;
2549 gboolean canceled = FALSE;
2550 const gchar *filename = NULL;
2551 gchar *save_multiple_str = NULL;
2553 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2554 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2556 if (mime_parts == NULL) {
2557 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2558 if (mime_parts == NULL)
2563 /* prepare dialog */
2564 if (mime_parts->next == NULL) {
2565 /* only one attachment selected */
2566 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2567 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2568 filename = tny_mime_part_get_filename (mime_part);
2570 g_warning ("Tried to save a non-file attachment");
2574 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2575 g_list_length (mime_parts));
2578 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2579 GTK_FILE_CHOOSER_ACTION_SAVE);
2582 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2583 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2587 if (filename != NULL)
2588 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2591 /* if multiple, set multiple string */
2592 if (save_multiple_str) {
2593 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2597 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2598 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2600 if (!modest_utils_folder_writable (chooser_uri)) {
2601 hildon_banner_show_information
2602 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2606 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2607 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2609 if (tny_mime_part_is_attachment (mime_part)) {
2610 SaveMimePartPair *pair;
2612 if ((mime_parts->next != NULL) &&
2613 (tny_mime_part_get_filename (mime_part) == NULL))
2616 pair = g_slice_new0 (SaveMimePartPair);
2617 if (mime_parts->next == NULL) {
2618 pair->filename = g_strdup (chooser_uri);
2621 g_build_filename (chooser_uri,
2622 tny_mime_part_get_filename (mime_part), NULL);
2624 pair->part = g_object_ref (mime_part);
2625 files_to_save = g_list_prepend (files_to_save, pair);
2629 g_free (chooser_uri);
2632 gtk_widget_destroy (save_dialog);
2635 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2636 g_list_free (mime_parts);
2639 if (files_to_save != NULL) {
2640 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2641 info->pairs = files_to_save;
2642 info->result = TRUE;
2643 save_mime_parts_to_file_with_checks (info);
2648 show_remove_attachment_information (gpointer userdata)
2650 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2651 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2653 /* We're outside the main lock */
2654 gdk_threads_enter ();
2656 if (priv->remove_attachment_banner != NULL) {
2657 gtk_widget_destroy (priv->remove_attachment_banner);
2658 g_object_unref (priv->remove_attachment_banner);
2661 priv->remove_attachment_banner = g_object_ref (
2662 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2664 gdk_threads_leave ();
2670 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2672 ModestMsgViewWindowPrivate *priv;
2673 GList *mime_parts = NULL, *node;
2674 gchar *confirmation_message;
2678 /* TnyFolder *folder; */
2680 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2681 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2684 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2686 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2688 /* Remove already purged messages from mime parts list */
2690 while (node != NULL) {
2691 TnyMimePart *part = TNY_MIME_PART (node->data);
2692 if (tny_mime_part_is_purged (part)) {
2693 GList *deleted_node = node;
2694 node = g_list_next (node);
2695 g_object_unref (part);
2696 mime_parts = g_list_delete_link (mime_parts, deleted_node);
2698 node = g_list_next (node);
2702 if (mime_parts == NULL)
2705 n_attachments = g_list_length (mime_parts);
2706 if (n_attachments == 1) {
2707 const gchar *filename;
2709 if (TNY_IS_MSG (mime_parts->data)) {
2711 header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2712 filename = tny_header_get_subject (header);
2713 g_object_unref (header);
2714 if (filename == NULL)
2715 filename = _("mail_va_no_subject");
2717 filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2719 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2721 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2722 "mcen_nc_purge_files_text",
2723 n_attachments), n_attachments);
2725 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2726 confirmation_message);
2727 g_free (confirmation_message);
2729 if (response != GTK_RESPONSE_OK)
2732 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2733 /* folder = tny_msg_get_folder (msg); */
2734 /* tny_msg_uncache_attachments (msg); */
2735 /* tny_folder_refresh (folder, NULL); */
2736 /* g_object_unref (folder); */
2738 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2739 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2740 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2743 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2744 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2745 tny_msg_rewrite_cache (msg);
2746 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2748 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2749 g_list_free (mime_parts);
2751 if (priv->purge_timeout > 0) {
2752 g_source_remove (priv->purge_timeout);
2753 priv->purge_timeout = 0;
2756 if (priv->remove_attachment_banner) {
2757 gtk_widget_destroy (priv->remove_attachment_banner);
2758 g_object_unref (priv->remove_attachment_banner);
2759 priv->remove_attachment_banner = NULL;
2767 update_window_title (ModestMsgViewWindow *window)
2769 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2771 TnyHeader *header = NULL;
2772 const gchar *subject = NULL;
2774 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2777 header = tny_msg_get_header (msg);
2778 subject = tny_header_get_subject (header);
2779 g_object_unref (msg);
2782 if ((subject == NULL)||(subject[0] == '\0'))
2783 subject = _("mail_va_no_subject");
2785 gtk_window_set_title (GTK_WINDOW (window), subject);
2789 static void on_move_focus (GtkWidget *widget,
2790 GtkDirectionType direction,
2793 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");