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);
429 /* scale the icon, because it won't be shown unless it's
430 * 64 x 54 -- hildon quirk. this looks a bit ugly now,
431 * so waiting for correctly sized icons, then this scaling
432 * code can disappear -- djcb
435 gdk_pixbuf_scale_simple (window_icon, 64, 54, GDK_INTERP_BILINEAR);
437 g_warning ("setting scaled icon");
438 gtk_window_set_icon (GTK_WINDOW (obj), scaled);
439 g_object_unref (scaled);
441 g_object_unref (window_icon);
444 hildon_program_add_window (hildon_program_get_instance(),
447 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
448 GTK_WINDOW(obj),"applications_email_viewer");
453 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
455 ModestMsgViewWindowPrivate *priv = NULL;
457 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
459 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
461 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
463 if (priv->progress_bar_timeout > 0) {
464 g_source_remove (priv->progress_bar_timeout);
465 priv->progress_bar_timeout = 0;
472 set_toolbar_mode (ModestMsgViewWindow *self,
473 ModestToolBarModes mode)
475 ModestWindowPrivate *parent_priv;
476 ModestMsgViewWindowPrivate *priv;
477 /* GtkWidget *widget = NULL; */
479 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
481 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
482 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
484 /* Sets current toolbar mode */
485 priv->current_toolbar_mode = mode;
487 /* Update toolbar dimming state */
488 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
491 case TOOLBAR_MODE_NORMAL:
492 if (priv->progress_toolitem) {
493 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
494 gtk_widget_hide (priv->progress_toolitem);
497 if (priv->progress_bar)
498 gtk_widget_hide (priv->progress_bar);
500 if (priv->cancel_toolitem)
501 gtk_widget_hide (priv->cancel_toolitem);
503 if (priv->prev_toolitem)
504 gtk_widget_show (priv->prev_toolitem);
506 if (priv->next_toolitem)
507 gtk_widget_show (priv->next_toolitem);
509 /* Hide toolbar if optimized view is enabled */
510 if (priv->optimized_view) {
511 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
512 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
516 case TOOLBAR_MODE_TRANSFER:
517 if (priv->prev_toolitem)
518 gtk_widget_hide (priv->prev_toolitem);
520 if (priv->next_toolitem)
521 gtk_widget_hide (priv->next_toolitem);
523 if (priv->progress_bar)
524 gtk_widget_show (priv->progress_bar);
526 if (priv->progress_toolitem) {
527 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
528 gtk_widget_show (priv->progress_toolitem);
531 if (priv->cancel_toolitem)
532 gtk_widget_show (priv->cancel_toolitem);
534 /* Show toolbar if it's hiden (optimized view ) */
535 if (priv->optimized_view) {
536 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
537 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
542 g_return_if_reached ();
549 menubar_to_menu (GtkUIManager *ui_manager)
551 GtkWidget *main_menu;
553 /* Get the menubar from the UI manager */
554 main_menu = gtk_ui_manager_get_widget (ui_manager, "/MenuBar");
560 init_window (ModestMsgViewWindow *obj)
562 GtkWidget *main_vbox;
563 ModestMsgViewWindowPrivate *priv;
564 ModestWindowPrivate *parent_priv;
566 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
567 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
569 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
570 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
571 main_vbox = gtk_vbox_new (FALSE, 6);
573 #ifdef MODEST_USE_MOZEMBED
574 priv->main_scroll = priv->msg_view;
575 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
577 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
578 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
580 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
581 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
582 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
584 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
585 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
587 priv->find_toolbar = hildon_find_toolbar_new (NULL);
588 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
589 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
591 gtk_widget_show_all (GTK_WIDGET(main_vbox));
595 modest_msg_view_window_disconnect_signals (ModestWindow *self)
597 ModestMsgViewWindowPrivate *priv;
598 ModestHeaderView *header_view = NULL;
599 ModestWindow *main_window = NULL;
601 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
603 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
604 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
605 priv->clipboard_change_handler))
606 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
607 priv->clipboard_change_handler);
609 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
610 priv->queue_change_handler))
611 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
612 priv->queue_change_handler);
614 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
615 priv->account_removed_handler))
616 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
617 priv->account_removed_handler);
619 if (priv->header_model) {
620 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
621 priv->row_changed_handler))
622 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
623 priv->row_changed_handler);
625 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
626 priv->row_deleted_handler))
627 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
628 priv->row_deleted_handler);
630 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
631 priv->row_inserted_handler))
632 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
633 priv->row_inserted_handler);
635 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
636 priv->rows_reordered_handler))
637 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
638 priv->rows_reordered_handler);
641 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
642 priv->sighandlers = NULL;
644 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
645 FALSE); /* don't create */
649 header_view = MODEST_HEADER_VIEW(
650 modest_main_window_get_child_widget(
651 MODEST_MAIN_WINDOW(main_window),
652 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
653 if (header_view == NULL)
656 modest_header_view_remove_observer(header_view,
657 MODEST_HEADER_VIEW_OBSERVER(self));
661 modest_msg_view_window_finalize (GObject *obj)
663 ModestMsgViewWindowPrivate *priv;
665 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
667 /* Sanity check: shouldn't be needed, the window mgr should
668 call this function before */
669 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
671 if (priv->header_model != NULL) {
672 g_object_unref (priv->header_model);
673 priv->header_model = NULL;
676 if (priv->progress_bar_timeout > 0) {
677 g_source_remove (priv->progress_bar_timeout);
678 priv->progress_bar_timeout = 0;
681 if (priv->remove_attachment_banner) {
682 gtk_widget_destroy (priv->remove_attachment_banner);
683 g_object_unref (priv->remove_attachment_banner);
684 priv->remove_attachment_banner = NULL;
687 if (priv->purge_timeout > 0) {
688 g_source_remove (priv->purge_timeout);
689 priv->purge_timeout = 0;
692 if (priv->row_reference) {
693 gtk_tree_row_reference_free (priv->row_reference);
694 priv->row_reference = NULL;
697 if (priv->next_row_reference) {
698 gtk_tree_row_reference_free (priv->next_row_reference);
699 priv->next_row_reference = NULL;
703 g_free (priv->msg_uid);
704 priv->msg_uid = NULL;
707 G_OBJECT_CLASS(parent_class)->finalize (obj);
711 select_next_valid_row (GtkTreeModel *model,
712 GtkTreeRowReference **row_reference,
715 GtkTreeIter tmp_iter;
716 GtkTreePath *path, *next;
717 gboolean retval = FALSE;
719 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
721 path = gtk_tree_row_reference_get_path (*row_reference);
722 gtk_tree_model_get_iter (model, &tmp_iter, path);
723 gtk_tree_row_reference_free (*row_reference);
724 *row_reference = NULL;
726 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
727 next = gtk_tree_model_get_path (model, &tmp_iter);
728 *row_reference = gtk_tree_row_reference_new (model, next);
730 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
731 next = gtk_tree_model_get_path (model, &tmp_iter);
733 /* Ensure that we are not selecting the same */
734 if (gtk_tree_path_compare (path, next) != 0) {
735 *row_reference = gtk_tree_row_reference_new (model, next);
741 gtk_tree_path_free (path);
746 /* TODO: This should be in _init(), with the parameters as properties. */
748 modest_msg_view_window_construct (ModestMsgViewWindow *self,
749 const gchar *modest_account_name,
750 const gchar *msg_uid)
753 ModestMsgViewWindowPrivate *priv = NULL;
754 ModestWindowPrivate *parent_priv = NULL;
755 ModestDimmingRulesGroup *menu_rules_group = NULL;
756 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
757 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
759 obj = G_OBJECT (self);
760 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
761 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
763 priv->msg_uid = g_strdup (msg_uid);
766 parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
767 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
768 gtk_widget_show (parent_priv->menubar);
769 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
771 menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
772 toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
773 clipboard_rules_group = modest_dimming_rules_group_new ("ModestClipboardDimmingRules", FALSE);
775 /* Add common dimming rules */
776 modest_dimming_rules_group_add_rules (menu_rules_group,
777 modest_msg_view_menu_dimming_entries,
778 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
779 MODEST_WINDOW (self));
780 modest_dimming_rules_group_add_rules (toolbar_rules_group,
781 modest_msg_view_toolbar_dimming_entries,
782 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
783 MODEST_WINDOW (self));
784 modest_dimming_rules_group_add_rules (clipboard_rules_group,
785 modest_msg_view_clipboard_dimming_entries,
786 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
787 MODEST_WINDOW (self));
789 /* Insert dimming rules group for this window */
790 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
791 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
792 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
793 g_object_unref (menu_rules_group);
794 g_object_unref (toolbar_rules_group);
795 g_object_unref (clipboard_rules_group);
797 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
799 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
801 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);
802 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
803 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
804 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
805 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
806 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
807 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
808 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
809 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
810 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
811 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
813 g_signal_connect (G_OBJECT (obj), "key-release-event",
814 G_CALLBACK (modest_msg_view_window_key_event),
817 g_signal_connect (G_OBJECT (obj), "key-press-event",
818 G_CALLBACK (modest_msg_view_window_key_event),
821 g_signal_connect (G_OBJECT (obj), "window-state-event",
822 G_CALLBACK (modest_msg_view_window_window_state_event),
825 g_signal_connect (G_OBJECT (obj), "move-focus",
826 G_CALLBACK (on_move_focus), obj);
828 /* Mail Operation Queue */
829 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
831 G_CALLBACK (on_queue_changed),
834 /* Account manager */
835 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
837 G_CALLBACK(on_account_removed),
840 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
842 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
843 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
844 priv->last_search = NULL;
846 /* Init the clipboard actions dim status */
847 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
849 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
851 /* Check toolbar dimming rules */
852 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
853 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), "ModestClipboardDimmingRules");
857 /* FIXME: parameter checks */
859 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
860 const gchar *modest_account_name,
861 const gchar *msg_uid,
863 GtkTreeRowReference *row_reference)
865 ModestMsgViewWindow *window = NULL;
866 ModestMsgViewWindowPrivate *priv = NULL;
867 TnyFolder *header_folder = NULL;
868 ModestHeaderView *header_view = NULL;
869 ModestWindow *main_window = NULL;
870 ModestWindowMgr *mgr = NULL;
872 mgr = modest_runtime_get_window_mgr ();
873 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
874 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
876 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
878 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
880 /* Remember the message list's TreeModel so we can detect changes
881 * and change the list selection when necessary: */
883 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
885 g_warning ("%s: BUG: no main window", __FUNCTION__);
889 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
890 MODEST_MAIN_WINDOW(main_window),
891 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
892 if (header_view != NULL){
893 header_folder = modest_header_view_get_folder(header_view);
894 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
895 g_assert(header_folder != NULL);
896 priv->header_folder_id = tny_folder_get_id(header_folder);
897 g_assert(priv->header_folder_id != NULL);
898 g_object_unref(header_folder);
901 priv->header_model = g_object_ref(model);
902 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
903 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
904 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
906 priv->row_changed_handler = g_signal_connect(
907 GTK_TREE_MODEL(model), "row-changed",
908 G_CALLBACK(modest_msg_view_window_on_row_changed),
910 priv->row_deleted_handler = g_signal_connect(
911 GTK_TREE_MODEL(model), "row-deleted",
912 G_CALLBACK(modest_msg_view_window_on_row_deleted),
914 priv->row_inserted_handler = g_signal_connect (
915 GTK_TREE_MODEL(model), "row-inserted",
916 G_CALLBACK(modest_msg_view_window_on_row_inserted),
918 priv->rows_reordered_handler = g_signal_connect(
919 GTK_TREE_MODEL(model), "rows-reordered",
920 G_CALLBACK(modest_msg_view_window_on_row_reordered),
923 if (header_view != NULL){
924 modest_header_view_add_observer(header_view,
925 MODEST_HEADER_VIEW_OBSERVER(window));
928 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
929 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
930 gtk_widget_show_all (GTK_WIDGET (window));
932 modest_msg_view_window_update_priority (window);
934 /* Check toolbar dimming rules */
935 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
937 return MODEST_WINDOW(window);
941 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
942 const gchar *modest_account_name,
943 const gchar *msg_uid)
945 ModestMsgViewWindow *window = NULL;
946 ModestMsgViewWindowPrivate *priv = NULL;
947 ModestWindowMgr *mgr = NULL;
949 mgr = modest_runtime_get_window_mgr ();
950 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
951 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
952 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
954 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
956 /* Remember that this is a search result,
957 * so we can disable some UI appropriately: */
958 priv->is_search_result = TRUE;
960 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
961 update_window_title (window);
963 return MODEST_WINDOW(window);
967 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
968 const gchar *modest_account_name,
969 const gchar *msg_uid)
972 ModestMsgViewWindowPrivate *priv;
973 ModestWindowMgr *mgr = NULL;
975 g_return_val_if_fail (msg, NULL);
976 mgr = modest_runtime_get_window_mgr ();
977 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
978 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
979 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
980 modest_account_name, msg_uid);
982 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
983 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
985 return MODEST_WINDOW(obj);
988 void modest_msg_view_window_on_row_changed(
989 GtkTreeModel *header_model,
992 ModestMsgViewWindow *window){
993 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
996 void modest_msg_view_window_on_row_deleted(
997 GtkTreeModel *header_model,
999 ModestMsgViewWindow *window){
1000 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1003 /* On insertions we check if the folder still has the message we are
1004 * showing or do not. If do not, we do nothing. Which means we are still
1005 * not attached to any header folder and thus next/prev buttons are
1006 * still dimmed. Once the message that is shown by msg-view is found, the
1007 * new model of header-view will be attached and the references will be set.
1008 * On each further insertions dimming rules will be checked. However
1009 * this requires extra CPU time at least works.
1010 * (An message might be deleted from TnyFolder and thus will not be
1011 * inserted into the model again for example if it is removed by the
1012 * imap server and the header view is refreshed.)
1014 void modest_msg_view_window_on_row_inserted(
1015 GtkTreeModel *new_model,
1016 GtkTreePath *tree_path,
1017 GtkTreeIter *tree_iter,
1018 ModestMsgViewWindow *window){
1019 ModestMsgViewWindowPrivate *priv = NULL;
1020 TnyHeader *header = NULL;
1023 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1025 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1027 /* If we already has a model attached then the message shown by
1028 * msg-view is in it, and thus we do not need any actions but
1029 * to check the dimming rules.*/
1030 if(priv->header_model != NULL){
1031 gtk_tree_row_reference_free(priv->next_row_reference);
1032 priv->next_row_reference = gtk_tree_row_reference_copy(
1033 priv->row_reference);
1034 select_next_valid_row (priv->header_model,
1035 &(priv->next_row_reference), FALSE);
1036 modest_ui_actions_check_toolbar_dimming_rules (
1037 MODEST_WINDOW (window));
1041 /* Check if the newly inserted message is the same we are actually
1042 * showing. IF not, we should remain detached from the header model
1043 * and thus prev and next toolbarbuttons should remain dimmed. */
1044 gtk_tree_model_get (new_model, tree_iter,
1045 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1046 uid = modest_tny_folder_get_header_unique_id(header);
1047 g_object_unref(G_OBJECT(header));
1049 if(!g_str_equal(priv->msg_uid, uid)){
1055 /* Setup row_reference for the actual msg. */
1056 priv->row_reference = gtk_tree_row_reference_new(
1057 new_model, tree_path);
1058 if(priv->row_reference == NULL){
1059 g_warning("No reference for msg header item.");
1063 /* Attach new_model and connect some callback to it to become able
1064 * to detect changes in header-view. */
1065 priv->header_model = g_object_ref(new_model);
1066 g_signal_connect (new_model, "row-changed",
1067 G_CALLBACK (modest_msg_view_window_on_row_changed),
1069 g_signal_connect (new_model, "row-deleted",
1070 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1072 g_signal_connect (new_model, "rows-reordered",
1073 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1076 /* Now set up next_row_reference. */
1077 priv->next_row_reference = gtk_tree_row_reference_copy(
1078 priv->row_reference);
1079 select_next_valid_row (priv->header_model,
1080 &(priv->next_row_reference), FALSE);
1082 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1085 void modest_msg_view_window_on_row_reordered(
1086 GtkTreeModel *header_model,
1090 ModestMsgViewWindow *window){
1091 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1094 /* The modest_msg_view_window_update_model_replaced implements update
1095 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1096 * actually belongs to the header-view is the same as the TnyFolder of
1097 * the message of msg-view or not. If they are different, there is
1098 * nothing to do. If they are the same, then the model has replaced and
1099 * the reference in msg-view shall be replaced from the old model to
1100 * the new model. In this case the view will be detached from it's
1101 * header folder. From this point the next/prev buttons are dimmed.
1103 void modest_msg_view_window_update_model_replaced(
1104 ModestHeaderViewObserver *observer,
1105 GtkTreeModel *model,
1106 const gchar *tny_folder_id){
1107 ModestMsgViewWindowPrivate *priv = NULL;
1108 ModestMsgViewWindow *window = NULL;
1110 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1111 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1113 window = MODEST_MSG_VIEW_WINDOW(observer);
1114 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1116 /* If there is an other folder in the header-view then we do
1117 * not care about it's model (msg list). Else if the
1118 * header-view shows the folder the msg shown by us is in, we
1119 * shall replace our model reference and make some check. */
1120 if(tny_folder_id == NULL || !g_str_equal(tny_folder_id, priv->header_folder_id))
1123 /* Model is changed(replaced), so we should forget the old
1124 * one. Because there might be other references and there
1125 * might be some change on the model even if we unreferenced
1126 * it, we need to disconnect our signals here. */
1127 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1128 priv->row_changed_handler))
1129 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1130 priv->row_changed_handler);
1131 priv->row_changed_handler = 0;
1132 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1133 priv->row_deleted_handler))
1134 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1135 priv->row_deleted_handler);
1136 priv->row_deleted_handler = 0;
1137 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1138 priv->row_inserted_handler))
1139 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1140 priv->row_inserted_handler);
1141 priv->row_inserted_handler = 0;
1142 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1143 priv->rows_reordered_handler))
1144 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1145 priv->rows_reordered_handler);
1146 priv->rows_reordered_handler = 0;
1147 g_object_unref(priv->header_model);
1148 priv->header_model = NULL;
1149 g_object_unref(priv->row_reference);
1150 priv->row_reference = NULL;
1151 g_object_unref(priv->next_row_reference);
1152 priv->next_row_reference = NULL;
1154 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1156 g_assert(model != NULL);
1158 /* Also we must connect to the new model for row insertions.
1159 * Only for insertions now. We will need other ones only after
1160 * the msg is show by msg-view is added to the new model. */
1161 priv->row_inserted_handler = g_signal_connect (
1162 model, "row-inserted",
1163 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1168 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1170 ModestMsgViewWindowPrivate *priv= NULL;
1172 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1173 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1175 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1179 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1181 ModestMsgViewWindowPrivate *priv= NULL;
1183 TnyHeader *header = NULL;
1184 GtkTreePath *path = NULL;
1187 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1188 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1190 /* If the message was not obtained from a treemodel,
1191 * for instance if it was opened directly by the search UI:
1193 if (priv->header_model == NULL) {
1194 msg = modest_msg_view_window_get_message (self);
1196 header = tny_msg_get_header (msg);
1197 g_object_unref (msg);
1202 /* Get iter of the currently selected message in the header view: */
1203 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1204 * instead of sometimes retrieving it from the header view?
1205 * Then we wouldn't be dependent on the message actually still being selected
1206 * in the header view. murrayc. */
1207 if (!gtk_tree_row_reference_valid (priv->row_reference))
1209 path = gtk_tree_row_reference_get_path (priv->row_reference);
1210 g_return_val_if_fail (path != NULL, NULL);
1211 gtk_tree_model_get_iter (priv->header_model,
1215 /* Get current message header */
1216 gtk_tree_model_get (priv->header_model, &iter,
1217 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1220 gtk_tree_path_free (path);
1225 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1227 ModestMsgViewWindowPrivate *priv;
1229 g_return_val_if_fail (self, NULL);
1231 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1233 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1237 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1239 ModestMsgViewWindowPrivate *priv;
1241 g_return_val_if_fail (self, NULL);
1243 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1245 return (const gchar*) priv->msg_uid;
1249 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1252 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1253 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1254 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1258 is_active = gtk_toggle_action_get_active (toggle);
1261 gtk_widget_show (priv->find_toolbar);
1262 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1264 gtk_widget_hide (priv->find_toolbar);
1267 /* update the toggle buttons status */
1268 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1269 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1270 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1271 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1276 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1277 ModestMsgViewWindow *obj)
1279 GtkToggleAction *toggle;
1280 ModestWindowPrivate *parent_priv;
1281 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1283 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1284 gtk_toggle_action_set_active (toggle, FALSE);
1288 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1289 ModestMsgViewWindow *obj)
1291 gchar *current_search;
1292 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1294 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1295 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1299 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1301 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1302 g_free (current_search);
1303 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1307 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1309 g_free (priv->last_search);
1310 priv->last_search = g_strdup (current_search);
1311 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1314 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1315 g_free (priv->last_search);
1316 priv->last_search = NULL;
1318 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1321 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1322 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1323 g_free (priv->last_search);
1324 priv->last_search = NULL;
1326 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1330 g_free (current_search);
1335 modest_msg_view_window_set_zoom (ModestWindow *window,
1338 ModestMsgViewWindowPrivate *priv;
1339 ModestWindowPrivate *parent_priv;
1340 GtkAction *action = NULL;
1341 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1343 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1345 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1346 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1347 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1349 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1350 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1352 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1356 modest_msg_view_window_get_zoom (ModestWindow *window)
1358 ModestMsgViewWindowPrivate *priv;
1360 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1362 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1363 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1367 modest_msg_view_window_zoom_plus (ModestWindow *window)
1369 ModestWindowPrivate *parent_priv;
1370 GtkRadioAction *zoom_radio_action;
1371 GSList *group, *node;
1373 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1374 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1375 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1377 group = gtk_radio_action_get_group (zoom_radio_action);
1379 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1380 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1384 for (node = group; node != NULL; node = g_slist_next (node)) {
1385 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1386 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1394 modest_msg_view_window_zoom_minus (ModestWindow *window)
1396 ModestWindowPrivate *parent_priv;
1397 GtkRadioAction *zoom_radio_action;
1398 GSList *group, *node;
1400 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1401 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1402 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1404 group = gtk_radio_action_get_group (zoom_radio_action);
1406 for (node = group; node != NULL; node = g_slist_next (node)) {
1407 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1408 if (node->next != NULL) {
1409 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1412 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1422 modest_msg_view_window_key_event (GtkWidget *window,
1427 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1428 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1429 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1430 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1431 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1432 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1433 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1434 gboolean return_value;
1436 if (event->type == GDK_KEY_RELEASE) {
1437 GtkScrollType scroll_type;
1439 switch (event->keyval) {
1442 scroll_type = GTK_SCROLL_STEP_UP; break;
1445 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1447 case GDK_KP_Page_Up:
1448 scroll_type = GTK_SCROLL_PAGE_UP; break;
1450 case GDK_KP_Page_Down:
1451 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1454 scroll_type = GTK_SCROLL_START; break;
1457 scroll_type = GTK_SCROLL_END; break;
1458 default: scroll_type = GTK_SCROLL_NONE;
1461 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",
1462 scroll_type, FALSE, &return_value);
1473 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1476 ModestMsgViewWindowPrivate *priv;
1477 GtkTreeIter tmp_iter;
1478 gboolean is_last_selected;
1480 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1481 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1483 /*if no model (so no rows at all), then virtually we are the last*/
1484 if (!priv->header_model)
1487 path = gtk_tree_row_reference_get_path (priv->row_reference);
1491 is_last_selected = TRUE;
1492 while (is_last_selected) {
1494 gtk_tree_path_next (path);
1495 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1497 gtk_tree_model_get (priv->header_model, &tmp_iter,
1498 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1501 if (msg_is_visible (header, priv->is_outbox))
1502 is_last_selected = FALSE;
1503 g_object_unref(G_OBJECT(header));
1506 gtk_tree_path_free (path);
1507 return is_last_selected;
1511 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1513 ModestMsgViewWindowPrivate *priv;
1515 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1516 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1518 return priv->header_model != NULL;
1522 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1524 ModestMsgViewWindowPrivate *priv;
1526 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1527 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1529 return priv->is_search_result;
1533 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1535 return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1536 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1541 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1544 ModestMsgViewWindowPrivate *priv;
1545 gboolean is_first_selected;
1546 GtkTreeIter tmp_iter;
1547 /* gchar * path_string;*/
1549 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1550 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1552 /*if no model (so no rows at all), then virtually we are the first*/
1553 if (!priv->header_model)
1556 path = gtk_tree_row_reference_get_path (priv->row_reference);
1560 /* path_string = gtk_tree_path_to_string (path);
1561 is_first_selected = strcmp (path_string, "0");
1563 g_free (path_string);
1564 gtk_tree_path_free (path);
1566 return is_first_selected;*/
1568 is_first_selected = TRUE;
1569 while (is_first_selected) {
1571 if(!gtk_tree_path_prev (path))
1573 /* Here the 'if' is needless for logic, but let make sure
1574 * iter is valid for gtk_tree_model_get. */
1575 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1577 gtk_tree_model_get (priv->header_model, &tmp_iter,
1578 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1581 if (msg_is_visible (header, priv->is_outbox))
1582 is_first_selected = FALSE;
1583 g_object_unref(G_OBJECT(header));
1586 gtk_tree_path_free (path);
1587 return is_first_selected;
1591 * Reads the message whose summary item is @header. It takes care of
1592 * several things, among others:
1594 * If the message was not previously downloaded then ask the user
1595 * before downloading. If there is no connection launch the connection
1596 * dialog. Update toolbar dimming rules.
1598 * Returns: TRUE if the mail operation was started, otherwise if the
1599 * user do not want to download the message, or if the user do not
1600 * want to connect, then the operation is not issued
1603 message_reader (ModestMsgViewWindow *window,
1604 ModestMsgViewWindowPrivate *priv,
1606 GtkTreeRowReference *row_reference)
1608 ModestMailOperation *mail_op = NULL;
1609 gboolean already_showing = FALSE;
1610 ModestWindow *msg_window = NULL;
1611 ModestWindowMgr *mgr;
1613 g_return_val_if_fail (row_reference != NULL, FALSE);
1615 mgr = modest_runtime_get_window_mgr ();
1616 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1617 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1620 gtk_window_present (GTK_WINDOW (msg_window));
1621 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1625 /* Msg download completed */
1626 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1627 /* Ask the user if he wants to download the message if
1629 if (!tny_device_is_online (modest_runtime_get_device())) {
1630 TnyFolder *folder = NULL;
1631 GtkResponseType response;
1633 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1634 _("mcen_nc_get_msg"));
1635 if (response == GTK_RESPONSE_CANCEL)
1638 /* Offer the connection dialog if necessary */
1639 folder = tny_header_get_folder (header);
1641 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1642 TNY_FOLDER_STORE (folder))) {
1643 g_object_unref (folder);
1646 g_object_unref (folder);
1651 /* New mail operation */
1652 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(window),
1653 modest_ui_actions_get_msgs_full_error_handler,
1656 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1657 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, row_reference);
1658 g_object_unref (mail_op);
1660 /* Update toolbar dimming rules */
1661 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1667 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1669 ModestMsgViewWindowPrivate *priv;
1670 GtkTreePath *path= NULL;
1671 GtkTreeIter tmp_iter;
1673 gboolean retval = TRUE;
1674 GtkTreeRowReference *row_reference = NULL;
1676 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1677 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1679 /* Update the next row reference if it's not valid. This could
1680 happen if for example the header which it was pointing to,
1681 was deleted. The best place to do it is in the row-deleted
1682 handler but the tinymail model do not work like the glib
1683 tree models and reports the deletion when the row is still
1685 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1686 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1687 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1688 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1691 if (priv->next_row_reference)
1692 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1696 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1698 gtk_tree_model_get_iter (priv->header_model,
1701 gtk_tree_path_free (path);
1703 gtk_tree_model_get (priv->header_model, &tmp_iter,
1704 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1707 /* Read the message & show it */
1708 if (!message_reader (window, priv, header, row_reference)) {
1710 gtk_tree_row_reference_free (row_reference);
1714 g_object_unref (header);
1720 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1722 ModestMsgViewWindowPrivate *priv = NULL;
1723 TnyHeader *header = NULL;
1725 GtkTreePath *path = NULL;
1726 GtkTreeRowReference *row_reference = NULL;
1728 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1729 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1731 /* Check that the model is not empty */
1732 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1735 /* Get the header */
1736 gtk_tree_model_get (priv->header_model,
1738 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1740 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1741 if (!msg_is_visible (header, priv->is_outbox)) {
1742 g_object_unref (header);
1743 return modest_msg_view_window_select_next_message (self);
1746 path = gtk_tree_model_get_path (priv->header_model, &iter);
1747 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1748 gtk_tree_path_free (path);
1750 /* Read the message & show it */
1751 message_reader (self, priv, header, row_reference);
1754 g_object_unref (header);
1760 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1762 ModestMsgViewWindowPrivate *priv = NULL;
1764 GtkTreeRowReference *row_reference = NULL;
1766 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1767 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1769 /* Return inmediatly if there is no header model */
1770 if (!priv->header_model)
1773 path = gtk_tree_row_reference_get_path (priv->row_reference);
1774 while (gtk_tree_path_prev (path)) {
1778 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1779 gtk_tree_model_get (priv->header_model, &iter,
1780 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1784 if (!msg_is_visible (header, priv->is_outbox)) {
1785 g_object_unref (header);
1789 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1790 /* Read the message & show it */
1791 if (!message_reader (window, priv, header, row_reference)) {
1792 gtk_tree_row_reference_free (row_reference);
1793 g_object_unref (header);
1797 gtk_tree_path_free (path);
1798 g_object_unref (header);
1803 gtk_tree_path_free (path);
1808 view_msg_cb (ModestMailOperation *mail_op,
1815 ModestMsgViewWindow *self = NULL;
1816 ModestMsgViewWindowPrivate *priv = NULL;
1817 GtkTreeRowReference *row_reference = NULL;
1819 /* If there was any error */
1820 row_reference = (GtkTreeRowReference *) user_data;
1821 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1822 gtk_tree_row_reference_free (row_reference);
1826 /* Get the window */
1827 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1828 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1829 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1831 /* Update the row reference */
1832 gtk_tree_row_reference_free (priv->row_reference);
1833 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1834 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1835 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1836 gtk_tree_row_reference_free (row_reference);
1838 /* Mark header as read */
1839 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1840 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1842 /* Set new message */
1843 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1844 modest_msg_view_window_update_priority (self);
1845 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1846 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1848 /* Set the new message uid of the window */
1849 if (priv->msg_uid) {
1850 g_free (priv->msg_uid);
1851 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1854 /* Notify the observers */
1855 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1856 0, priv->header_model, priv->row_reference);
1858 /* Free new references */
1859 g_object_unref (self);
1863 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1865 ModestMsgViewWindowPrivate *priv;
1867 TnyFolderType folder_type;
1869 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1871 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1873 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1877 folder = tny_msg_get_folder (msg);
1879 folder_type = tny_folder_get_folder_type (folder);
1880 g_object_unref (folder);
1882 g_object_unref (msg);
1890 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1892 ModestMsgViewWindowPrivate *priv;
1893 TnyHeaderFlags flags = 0;
1895 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1897 if (priv->header_model) {
1900 GtkTreePath *path = NULL;
1902 path = gtk_tree_row_reference_get_path (priv->row_reference);
1903 g_return_if_fail (path != NULL);
1904 gtk_tree_model_get_iter (priv->header_model,
1906 gtk_tree_row_reference_get_path (priv->row_reference));
1908 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1911 flags = tny_header_get_flags (header);
1912 g_object_unref(G_OBJECT(header));
1914 gtk_tree_path_free (path);
1917 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1922 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1924 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1925 ModestWindowPrivate *parent_priv;
1926 ModestWindowMgr *mgr;
1927 gboolean is_fullscreen;
1928 GtkAction *fs_toggle_action;
1931 mgr = modest_runtime_get_window_mgr ();
1932 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1934 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1936 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1937 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1938 if (is_fullscreen != active) {
1939 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1948 set_homogeneous (GtkWidget *widget,
1951 if (GTK_IS_TOOL_ITEM (widget)) {
1952 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1953 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1958 modest_msg_view_window_show_toolbar (ModestWindow *self,
1959 gboolean show_toolbar)
1961 ModestMsgViewWindowPrivate *priv = NULL;
1962 ModestWindowPrivate *parent_priv;
1963 GtkWidget *reply_button = NULL, *menu = NULL;
1964 GtkWidget *placeholder = NULL;
1966 const gchar *action_name;
1969 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1970 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1972 /* Set optimized view status */
1973 priv->optimized_view = !show_toolbar;
1975 if (!parent_priv->toolbar) {
1976 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1978 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1980 /* Set homogeneous toolbar */
1981 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar),
1982 set_homogeneous, NULL);
1984 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1985 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1986 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1987 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1988 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1989 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1990 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1991 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1993 /* Add ProgressBar (Transfer toolbar) */
1994 priv->progress_bar = modest_progress_bar_widget_new ();
1995 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1996 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1997 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1998 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1999 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2001 /* Connect cancel 'clicked' signal to abort progress mode */
2002 g_signal_connect(priv->cancel_toolitem, "clicked",
2003 G_CALLBACK(cancel_progressbar),
2006 /* Add it to the observers list */
2007 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2010 hildon_window_add_toolbar (HILDON_WINDOW (self),
2011 GTK_TOOLBAR (parent_priv->toolbar));
2013 /* Set reply button tap and hold menu */
2014 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2015 "/ToolBar/ToolbarMessageReply");
2016 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2017 "/ToolbarReplyCSM");
2018 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2022 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2023 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2024 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2026 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2027 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2028 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2030 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2033 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2034 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2037 /* Update also the actions (to update the toggles in the
2038 menus), we have to do it manually because some other window
2039 of the same time could have changed it (remember that the
2040 toolbar fullscreen mode is shared by all the windows of the
2042 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2043 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2045 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2047 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2048 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2053 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2055 ModestMsgViewWindow *window)
2057 if (!GTK_WIDGET_VISIBLE (window))
2060 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
2064 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2066 ModestMsgViewWindowPrivate *priv;
2068 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2069 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2071 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2075 cancel_progressbar (GtkToolButton *toolbutton,
2076 ModestMsgViewWindow *self)
2079 ModestMsgViewWindowPrivate *priv;
2081 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2083 /* Get operation observers and cancel its current operation */
2084 tmp = priv->progress_widgets;
2086 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2087 tmp=g_slist_next(tmp);
2091 observers_empty (ModestMsgViewWindow *self)
2094 ModestMsgViewWindowPrivate *priv;
2095 gboolean is_empty = TRUE;
2096 guint pending_ops = 0;
2098 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2099 tmp = priv->progress_widgets;
2101 /* Check all observers */
2102 while (tmp && is_empty) {
2103 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2104 is_empty = pending_ops == 0;
2106 tmp = g_slist_next(tmp);
2113 on_account_removed (TnyAccountStore *account_store,
2114 TnyAccount *account,
2117 /* Do nothing if it's a transport account, because we only
2118 show the messages of a store account */
2119 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2120 const gchar *parent_acc = NULL;
2121 const gchar *our_acc = NULL;
2123 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2124 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2126 /* Close this window if I'm showing a message of the removed account */
2127 if (strcmp (parent_acc, our_acc) == 0)
2128 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2133 on_mail_operation_started (ModestMailOperation *mail_op,
2136 ModestMsgViewWindow *self;
2137 ModestMailOperationTypeOperation op_type;
2139 ModestMsgViewWindowPrivate *priv;
2141 self = MODEST_MSG_VIEW_WINDOW (user_data);
2142 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2143 op_type = modest_mail_operation_get_type_operation (mail_op);
2144 tmp = priv->progress_widgets;
2146 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2147 set_toolbar_transfer_mode(self);
2149 modest_progress_object_add_operation (
2150 MODEST_PROGRESS_OBJECT (tmp->data),
2152 tmp = g_slist_next (tmp);
2158 on_mail_operation_finished (ModestMailOperation *mail_op,
2161 ModestMsgViewWindow *self;
2162 ModestMailOperationTypeOperation op_type;
2164 ModestMsgViewWindowPrivate *priv;
2166 self = MODEST_MSG_VIEW_WINDOW (user_data);
2167 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2168 op_type = modest_mail_operation_get_type_operation (mail_op);
2169 tmp = priv->progress_widgets;
2171 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2173 modest_progress_object_remove_operation (
2174 MODEST_PROGRESS_OBJECT (tmp->data),
2176 tmp = g_slist_next (tmp);
2179 /* If no more operations are being observed, NORMAL mode is enabled again */
2180 if (observers_empty (self)) {
2181 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2187 on_queue_changed (ModestMailOperationQueue *queue,
2188 ModestMailOperation *mail_op,
2189 ModestMailOperationQueueNotification type,
2190 ModestMsgViewWindow *self)
2192 ModestMsgViewWindowPrivate *priv;
2194 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2196 /* If this operations was created by another window, do nothing */
2197 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2200 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2201 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2203 "operation-started",
2204 G_CALLBACK (on_mail_operation_started),
2206 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2208 "operation-finished",
2209 G_CALLBACK (on_mail_operation_finished),
2211 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2212 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2214 "operation-started");
2215 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2217 "operation-finished");
2222 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2224 ModestMsgViewWindowPrivate *priv;
2225 GList *selected_attachments = NULL;
2227 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2228 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2230 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2232 return selected_attachments;
2236 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2238 ModestMsgViewWindowPrivate *priv;
2239 const gchar *msg_uid;
2240 gchar *attachment_uid = NULL;
2241 gint attachment_index = 0;
2244 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2245 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2246 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2248 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2249 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2250 attachment_index = g_list_index (attachments, mime_part);
2251 g_list_free (attachments);
2253 if (msg_uid && attachment_index >= 0) {
2254 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2257 if (mime_part == NULL) {
2258 gboolean error = FALSE;
2259 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2260 if (selected_attachments == NULL) {
2262 } else if (g_list_length (selected_attachments) > 1) {
2263 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2266 mime_part = (TnyMimePart *) selected_attachments->data;
2267 g_object_ref (mime_part);
2269 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2270 g_list_free (selected_attachments);
2275 g_object_ref (mime_part);
2278 if (tny_mime_part_is_purged (mime_part)) {
2279 g_object_unref (mime_part);
2283 if (!TNY_IS_MSG (mime_part)) {
2284 gchar *filepath = NULL;
2285 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2286 const gchar *content_type;
2287 TnyFsStream *temp_stream = NULL;
2288 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2291 if (temp_stream != NULL) {
2292 content_type = tny_mime_part_get_content_type (mime_part);
2293 tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
2295 /* make the file read-only */
2296 if (g_chmod(filepath, 0444) != 0)
2297 g_warning ("%s: failed to set file '%s' to read-only: %s",
2298 __FUNCTION__, filepath, strerror(errno));
2300 modest_platform_activate_file (filepath, content_type);
2301 g_object_unref (temp_stream);
2303 /* NOTE: files in the temporary area will be automatically
2304 * cleaned after some time if they are no longer in use */
2305 } else if (filepath != NULL) {
2306 /* the file may already exist but it isn't writable,
2307 * let's try to open it anyway */
2308 content_type = tny_mime_part_get_content_type (mime_part);
2309 modest_platform_activate_file (filepath, content_type);
2313 /* message attachment */
2314 TnyHeader *header = NULL;
2315 ModestWindowMgr *mgr;
2316 ModestWindow *msg_win = NULL;
2319 header = tny_msg_get_header (TNY_MSG (mime_part));
2320 mgr = modest_runtime_get_window_mgr ();
2321 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2324 if (msg_win) /* there is already a window for this uid; top it */
2325 gtk_window_present (GTK_WINDOW(msg_win));
2327 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2328 * thus, we don't do anything */
2329 g_warning ("window for is already being created");
2331 /* it's not found, so create a new window for it */
2332 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2333 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2335 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2336 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2337 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2338 modest_window_get_zoom (MODEST_WINDOW (window)));
2339 modest_window_mgr_register_window (mgr, msg_win);
2340 gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
2341 gtk_widget_show_all (GTK_WIDGET (msg_win));
2344 g_object_unref (mime_part);
2360 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2361 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2362 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2363 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2366 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2370 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2371 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2372 g_free (pair->filename);
2373 g_object_unref (pair->part);
2374 g_slice_free (SaveMimePartPair, pair);
2376 g_list_free (info->pairs);
2379 gtk_widget_destroy (info->banner);
2380 g_object_unref (info->banner);
2381 g_slice_free (SaveMimePartInfo, info);
2386 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2388 if (info->pairs != NULL) {
2389 save_mime_part_to_file (info);
2392 result = info->result;
2394 /* This is a GDK lock because we are an idle callback and
2395 * hildon_banner_show_information is or does Gtk+ code */
2397 gdk_threads_enter (); /* CHECKED */
2398 save_mime_part_info_free (info, TRUE);
2400 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2402 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2404 gdk_threads_leave (); /* CHECKED */
2411 save_mime_part_to_file (SaveMimePartInfo *info)
2413 GnomeVFSResult result;
2414 GnomeVFSHandle *handle;
2416 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2418 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2419 if (result == GNOME_VFS_OK) {
2420 stream = tny_vfs_stream_new (handle);
2421 tny_mime_part_decode_to_stream (pair->part, stream);
2422 g_object_unref (G_OBJECT (stream));
2423 g_object_unref (pair->part);
2424 g_slice_free (SaveMimePartPair, pair);
2425 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2426 info->result = TRUE;
2428 save_mime_part_info_free (info, FALSE);
2429 info->result = FALSE;
2432 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2437 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2439 gboolean is_ok = TRUE;
2440 gint replaced_files = 0;
2441 const GList *files = info->pairs;
2444 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2445 SaveMimePartPair *pair = iter->data;
2446 if (modest_utils_file_exists (pair->filename)) {
2450 if (replaced_files) {
2451 GtkWidget *confirm_overwrite_dialog;
2452 const gchar *message = (replaced_files == 1) ?
2453 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2454 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2455 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2458 gtk_widget_destroy (confirm_overwrite_dialog);
2462 save_mime_part_info_free (info, TRUE);
2464 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2465 _CS("sfil_ib_saving"));
2466 info->banner = g_object_ref (banner);
2467 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2468 g_object_unref (banner);
2475 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2477 gboolean clean_list = FALSE;
2478 ModestMsgViewWindowPrivate *priv;
2479 GList *files_to_save = NULL;
2480 GtkWidget *save_dialog = NULL;
2481 gchar *folder = NULL;
2482 gboolean canceled = FALSE;
2483 const gchar *filename = NULL;
2484 gchar *save_multiple_str = NULL;
2486 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2487 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2489 if (mime_parts == NULL) {
2490 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2491 if (mime_parts == NULL)
2496 /* prepare dialog */
2497 if (mime_parts->next == NULL) {
2498 /* only one attachment selected */
2499 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2500 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2501 filename = tny_mime_part_get_filename (mime_part);
2503 g_warning ("Tried to save a non-file attachment");
2507 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2508 g_list_length (mime_parts));
2511 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2512 GTK_FILE_CHOOSER_ACTION_SAVE);
2515 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2516 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2520 if (filename != NULL)
2521 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2524 /* if multiple, set multiple string */
2525 if (save_multiple_str) {
2526 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2530 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2531 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2533 if (!modest_utils_folder_writable (chooser_uri)) {
2534 hildon_banner_show_information
2535 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2539 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2540 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2542 if (tny_mime_part_is_attachment (mime_part)) {
2543 SaveMimePartPair *pair;
2545 if ((mime_parts->next != NULL) &&
2546 (tny_mime_part_get_filename (mime_part) == NULL))
2549 pair = g_slice_new0 (SaveMimePartPair);
2550 if (mime_parts->next == NULL) {
2551 pair->filename = g_strdup (chooser_uri);
2554 g_build_filename (chooser_uri,
2555 tny_mime_part_get_filename (mime_part), NULL);
2557 pair->part = g_object_ref (mime_part);
2558 files_to_save = g_list_prepend (files_to_save, pair);
2562 g_free (chooser_uri);
2565 gtk_widget_destroy (save_dialog);
2568 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2569 g_list_free (mime_parts);
2572 if (files_to_save != NULL) {
2573 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2574 info->pairs = files_to_save;
2575 info->result = TRUE;
2576 save_mime_parts_to_file_with_checks (info);
2581 show_remove_attachment_information (gpointer userdata)
2583 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2584 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2586 /* We're outside the main lock */
2587 gdk_threads_enter ();
2589 if (priv->remove_attachment_banner != NULL) {
2590 gtk_widget_destroy (priv->remove_attachment_banner);
2591 g_object_unref (priv->remove_attachment_banner);
2594 priv->remove_attachment_banner = g_object_ref (
2595 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2597 gdk_threads_leave ();
2603 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2605 ModestMsgViewWindowPrivate *priv;
2606 GList *mime_parts = NULL, *node;
2607 gchar *confirmation_message;
2611 /* TnyFolder *folder; */
2613 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2614 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2617 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2619 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2621 /* Remove already purged messages from mime parts list */
2623 while (node != NULL) {
2624 TnyMimePart *part = TNY_MIME_PART (node->data);
2625 if (tny_mime_part_is_purged (part)) {
2626 GList *deleted_node = node;
2627 node = g_list_next (node);
2628 g_object_unref (part);
2629 mime_parts = g_list_delete_link (mime_parts, deleted_node);
2631 node = g_list_next (node);
2635 if (mime_parts == NULL)
2638 n_attachments = g_list_length (mime_parts);
2639 if (n_attachments == 1) {
2640 const gchar *filename;
2642 if (TNY_IS_MSG (mime_parts->data)) {
2644 header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2645 filename = tny_header_get_subject (header);
2646 g_object_unref (header);
2647 if (filename == NULL)
2648 filename = _("mail_va_no_subject");
2650 filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2652 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2654 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2655 "mcen_nc_purge_files_text",
2656 n_attachments), n_attachments);
2658 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2659 confirmation_message);
2660 g_free (confirmation_message);
2662 if (response != GTK_RESPONSE_OK)
2665 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2666 /* folder = tny_msg_get_folder (msg); */
2667 /* tny_msg_uncache_attachments (msg); */
2668 /* tny_folder_refresh (folder, NULL); */
2669 /* g_object_unref (folder); */
2671 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2672 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2673 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2676 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2677 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2678 tny_msg_rewrite_cache (msg);
2679 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2681 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2682 g_list_free (mime_parts);
2684 if (priv->purge_timeout > 0) {
2685 g_source_remove (priv->purge_timeout);
2686 priv->purge_timeout = 0;
2689 if (priv->remove_attachment_banner) {
2690 gtk_widget_destroy (priv->remove_attachment_banner);
2691 g_object_unref (priv->remove_attachment_banner);
2692 priv->remove_attachment_banner = NULL;
2700 update_window_title (ModestMsgViewWindow *window)
2702 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2704 TnyHeader *header = NULL;
2705 const gchar *subject = NULL;
2707 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2710 header = tny_msg_get_header (msg);
2711 subject = tny_header_get_subject (header);
2712 g_object_unref (msg);
2715 if ((subject == NULL)||(subject[0] == '\0'))
2716 subject = _("mail_va_no_subject");
2718 gtk_window_set_title (GTK_WINDOW (window), subject);
2722 static void on_move_focus (GtkWidget *widget,
2723 GtkDirectionType direction,
2726 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");