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 gtk_window_set_icon (GTK_WINDOW (obj), scaled);
438 g_object_unref (scaled);
440 g_object_unref (window_icon);
443 hildon_program_add_window (hildon_program_get_instance(),
446 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
447 GTK_WINDOW(obj),"applications_email_viewer");
452 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
454 ModestMsgViewWindowPrivate *priv = NULL;
456 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
458 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
460 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
462 if (priv->progress_bar_timeout > 0) {
463 g_source_remove (priv->progress_bar_timeout);
464 priv->progress_bar_timeout = 0;
471 set_toolbar_mode (ModestMsgViewWindow *self,
472 ModestToolBarModes mode)
474 ModestWindowPrivate *parent_priv;
475 ModestMsgViewWindowPrivate *priv;
476 /* GtkWidget *widget = NULL; */
478 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
480 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
481 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
483 /* Sets current toolbar mode */
484 priv->current_toolbar_mode = mode;
486 /* Update toolbar dimming state */
487 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
490 case TOOLBAR_MODE_NORMAL:
491 if (priv->progress_toolitem) {
492 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
493 gtk_widget_hide (priv->progress_toolitem);
496 if (priv->progress_bar)
497 gtk_widget_hide (priv->progress_bar);
499 if (priv->cancel_toolitem)
500 gtk_widget_hide (priv->cancel_toolitem);
502 if (priv->prev_toolitem)
503 gtk_widget_show (priv->prev_toolitem);
505 if (priv->next_toolitem)
506 gtk_widget_show (priv->next_toolitem);
508 /* Hide toolbar if optimized view is enabled */
509 if (priv->optimized_view) {
510 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
511 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
515 case TOOLBAR_MODE_TRANSFER:
516 if (priv->prev_toolitem)
517 gtk_widget_hide (priv->prev_toolitem);
519 if (priv->next_toolitem)
520 gtk_widget_hide (priv->next_toolitem);
522 if (priv->progress_bar)
523 gtk_widget_show (priv->progress_bar);
525 if (priv->progress_toolitem) {
526 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
527 gtk_widget_show (priv->progress_toolitem);
530 if (priv->cancel_toolitem)
531 gtk_widget_show (priv->cancel_toolitem);
533 /* Show toolbar if it's hiden (optimized view ) */
534 if (priv->optimized_view) {
535 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
536 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
541 g_return_if_reached ();
548 menubar_to_menu (GtkUIManager *ui_manager)
550 GtkWidget *main_menu;
552 /* Get the menubar from the UI manager */
553 main_menu = gtk_ui_manager_get_widget (ui_manager, "/MenuBar");
559 init_window (ModestMsgViewWindow *obj)
561 GtkWidget *main_vbox;
562 ModestMsgViewWindowPrivate *priv;
563 ModestWindowPrivate *parent_priv;
565 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
566 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
568 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
569 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
570 main_vbox = gtk_vbox_new (FALSE, 6);
572 #ifdef MODEST_USE_MOZEMBED
573 priv->main_scroll = priv->msg_view;
574 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
576 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
577 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
579 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
580 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
581 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
583 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
584 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
586 priv->find_toolbar = hildon_find_toolbar_new (NULL);
587 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
588 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
590 gtk_widget_show_all (GTK_WIDGET(main_vbox));
594 modest_msg_view_window_disconnect_signals (ModestWindow *self)
596 ModestMsgViewWindowPrivate *priv;
597 ModestHeaderView *header_view = NULL;
598 ModestWindow *main_window = NULL;
600 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
602 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
603 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
604 priv->clipboard_change_handler))
605 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
606 priv->clipboard_change_handler);
608 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
609 priv->queue_change_handler))
610 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
611 priv->queue_change_handler);
613 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
614 priv->account_removed_handler))
615 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
616 priv->account_removed_handler);
618 if (priv->header_model) {
619 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
620 priv->row_changed_handler))
621 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
622 priv->row_changed_handler);
624 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
625 priv->row_deleted_handler))
626 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
627 priv->row_deleted_handler);
629 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
630 priv->row_inserted_handler))
631 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
632 priv->row_inserted_handler);
634 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
635 priv->rows_reordered_handler))
636 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
637 priv->rows_reordered_handler);
640 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
641 priv->sighandlers = NULL;
643 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
644 FALSE); /* don't create */
648 header_view = MODEST_HEADER_VIEW(
649 modest_main_window_get_child_widget(
650 MODEST_MAIN_WINDOW(main_window),
651 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
652 if (header_view == NULL)
655 modest_header_view_remove_observer(header_view,
656 MODEST_HEADER_VIEW_OBSERVER(self));
660 modest_msg_view_window_finalize (GObject *obj)
662 ModestMsgViewWindowPrivate *priv;
664 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
666 /* Sanity check: shouldn't be needed, the window mgr should
667 call this function before */
668 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
670 if (priv->header_model != NULL) {
671 g_object_unref (priv->header_model);
672 priv->header_model = NULL;
675 if (priv->progress_bar_timeout > 0) {
676 g_source_remove (priv->progress_bar_timeout);
677 priv->progress_bar_timeout = 0;
680 if (priv->remove_attachment_banner) {
681 gtk_widget_destroy (priv->remove_attachment_banner);
682 g_object_unref (priv->remove_attachment_banner);
683 priv->remove_attachment_banner = NULL;
686 if (priv->purge_timeout > 0) {
687 g_source_remove (priv->purge_timeout);
688 priv->purge_timeout = 0;
691 if (priv->row_reference) {
692 gtk_tree_row_reference_free (priv->row_reference);
693 priv->row_reference = NULL;
696 if (priv->next_row_reference) {
697 gtk_tree_row_reference_free (priv->next_row_reference);
698 priv->next_row_reference = NULL;
702 g_free (priv->msg_uid);
703 priv->msg_uid = NULL;
706 G_OBJECT_CLASS(parent_class)->finalize (obj);
710 select_next_valid_row (GtkTreeModel *model,
711 GtkTreeRowReference **row_reference,
714 GtkTreeIter tmp_iter;
715 GtkTreePath *path, *next;
716 gboolean retval = FALSE;
718 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
720 path = gtk_tree_row_reference_get_path (*row_reference);
721 gtk_tree_model_get_iter (model, &tmp_iter, path);
722 gtk_tree_row_reference_free (*row_reference);
723 *row_reference = NULL;
725 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
726 next = gtk_tree_model_get_path (model, &tmp_iter);
727 *row_reference = gtk_tree_row_reference_new (model, next);
729 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
730 next = gtk_tree_model_get_path (model, &tmp_iter);
732 /* Ensure that we are not selecting the same */
733 if (gtk_tree_path_compare (path, next) != 0) {
734 *row_reference = gtk_tree_row_reference_new (model, next);
740 gtk_tree_path_free (path);
745 /* TODO: This should be in _init(), with the parameters as properties. */
747 modest_msg_view_window_construct (ModestMsgViewWindow *self,
748 const gchar *modest_account_name,
749 const gchar *msg_uid)
752 ModestMsgViewWindowPrivate *priv = NULL;
753 ModestWindowPrivate *parent_priv = NULL;
754 ModestDimmingRulesGroup *menu_rules_group = NULL;
755 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
756 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
758 obj = G_OBJECT (self);
759 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
760 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
762 priv->msg_uid = g_strdup (msg_uid);
765 parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager);
766 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
767 gtk_widget_show (parent_priv->menubar);
768 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
770 menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE);
771 toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE);
772 clipboard_rules_group = modest_dimming_rules_group_new ("ModestClipboardDimmingRules", FALSE);
774 /* Add common dimming rules */
775 modest_dimming_rules_group_add_rules (menu_rules_group,
776 modest_msg_view_menu_dimming_entries,
777 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
778 MODEST_WINDOW (self));
779 modest_dimming_rules_group_add_rules (toolbar_rules_group,
780 modest_msg_view_toolbar_dimming_entries,
781 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
782 MODEST_WINDOW (self));
783 modest_dimming_rules_group_add_rules (clipboard_rules_group,
784 modest_msg_view_clipboard_dimming_entries,
785 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
786 MODEST_WINDOW (self));
788 /* Insert dimming rules group for this window */
789 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
790 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
791 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
792 g_object_unref (menu_rules_group);
793 g_object_unref (toolbar_rules_group);
794 g_object_unref (clipboard_rules_group);
796 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
798 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
800 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);
801 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
802 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
803 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
804 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
805 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
806 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
807 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
808 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
809 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
810 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
812 g_signal_connect (G_OBJECT (obj), "key-release-event",
813 G_CALLBACK (modest_msg_view_window_key_event),
816 g_signal_connect (G_OBJECT (obj), "key-press-event",
817 G_CALLBACK (modest_msg_view_window_key_event),
820 g_signal_connect (G_OBJECT (obj), "window-state-event",
821 G_CALLBACK (modest_msg_view_window_window_state_event),
824 g_signal_connect (G_OBJECT (obj), "move-focus",
825 G_CALLBACK (on_move_focus), obj);
827 /* Mail Operation Queue */
828 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
830 G_CALLBACK (on_queue_changed),
833 /* Account manager */
834 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
836 G_CALLBACK(on_account_removed),
839 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
841 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
842 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
843 priv->last_search = NULL;
845 /* Init the clipboard actions dim status */
846 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
848 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
850 /* Check toolbar dimming rules */
851 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
852 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), "ModestClipboardDimmingRules");
856 /* FIXME: parameter checks */
858 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
859 const gchar *modest_account_name,
860 const gchar *msg_uid,
862 GtkTreeRowReference *row_reference)
864 ModestMsgViewWindow *window = NULL;
865 ModestMsgViewWindowPrivate *priv = NULL;
866 TnyFolder *header_folder = NULL;
867 ModestHeaderView *header_view = NULL;
868 ModestWindow *main_window = NULL;
869 ModestWindowMgr *mgr = NULL;
871 mgr = modest_runtime_get_window_mgr ();
872 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
873 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
875 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
877 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
879 /* Remember the message list's TreeModel so we can detect changes
880 * and change the list selection when necessary: */
882 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
884 g_warning ("%s: BUG: no main window", __FUNCTION__);
888 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
889 MODEST_MAIN_WINDOW(main_window),
890 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
891 if (header_view != NULL){
892 header_folder = modest_header_view_get_folder(header_view);
893 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
894 g_assert(header_folder != NULL);
895 priv->header_folder_id = tny_folder_get_id(header_folder);
896 g_assert(priv->header_folder_id != NULL);
897 g_object_unref(header_folder);
900 priv->header_model = g_object_ref(model);
901 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
902 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
903 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
905 priv->row_changed_handler = g_signal_connect(
906 GTK_TREE_MODEL(model), "row-changed",
907 G_CALLBACK(modest_msg_view_window_on_row_changed),
909 priv->row_deleted_handler = g_signal_connect(
910 GTK_TREE_MODEL(model), "row-deleted",
911 G_CALLBACK(modest_msg_view_window_on_row_deleted),
913 priv->row_inserted_handler = g_signal_connect (
914 GTK_TREE_MODEL(model), "row-inserted",
915 G_CALLBACK(modest_msg_view_window_on_row_inserted),
917 priv->rows_reordered_handler = g_signal_connect(
918 GTK_TREE_MODEL(model), "rows-reordered",
919 G_CALLBACK(modest_msg_view_window_on_row_reordered),
922 if (header_view != NULL){
923 modest_header_view_add_observer(header_view,
924 MODEST_HEADER_VIEW_OBSERVER(window));
927 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
928 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
929 gtk_widget_show_all (GTK_WIDGET (window));
931 modest_msg_view_window_update_priority (window);
933 /* Check toolbar dimming rules */
934 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
936 return MODEST_WINDOW(window);
940 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
941 const gchar *modest_account_name,
942 const gchar *msg_uid)
944 ModestMsgViewWindow *window = NULL;
945 ModestMsgViewWindowPrivate *priv = NULL;
946 ModestWindowMgr *mgr = NULL;
948 mgr = modest_runtime_get_window_mgr ();
949 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
950 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
951 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
953 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
955 /* Remember that this is a search result,
956 * so we can disable some UI appropriately: */
957 priv->is_search_result = TRUE;
959 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
960 update_window_title (window);
962 return MODEST_WINDOW(window);
966 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
967 const gchar *modest_account_name,
968 const gchar *msg_uid)
971 ModestMsgViewWindowPrivate *priv;
972 ModestWindowMgr *mgr = NULL;
974 g_return_val_if_fail (msg, NULL);
975 mgr = modest_runtime_get_window_mgr ();
976 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
977 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
978 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
979 modest_account_name, msg_uid);
981 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
982 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
984 return MODEST_WINDOW(obj);
987 void modest_msg_view_window_on_row_changed(
988 GtkTreeModel *header_model,
991 ModestMsgViewWindow *window){
992 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
995 void modest_msg_view_window_on_row_deleted(
996 GtkTreeModel *header_model,
998 ModestMsgViewWindow *window){
999 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1002 /* On insertions we check if the folder still has the message we are
1003 * showing or do not. If do not, we do nothing. Which means we are still
1004 * not attached to any header folder and thus next/prev buttons are
1005 * still dimmed. Once the message that is shown by msg-view is found, the
1006 * new model of header-view will be attached and the references will be set.
1007 * On each further insertions dimming rules will be checked. However
1008 * this requires extra CPU time at least works.
1009 * (An message might be deleted from TnyFolder and thus will not be
1010 * inserted into the model again for example if it is removed by the
1011 * imap server and the header view is refreshed.)
1013 void modest_msg_view_window_on_row_inserted(
1014 GtkTreeModel *new_model,
1015 GtkTreePath *tree_path,
1016 GtkTreeIter *tree_iter,
1017 ModestMsgViewWindow *window){
1018 ModestMsgViewWindowPrivate *priv = NULL;
1019 TnyHeader *header = NULL;
1022 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1024 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1026 /* If we already has a model attached then the message shown by
1027 * msg-view is in it, and thus we do not need any actions but
1028 * to check the dimming rules.*/
1029 if(priv->header_model != NULL){
1030 gtk_tree_row_reference_free(priv->next_row_reference);
1031 priv->next_row_reference = gtk_tree_row_reference_copy(
1032 priv->row_reference);
1033 select_next_valid_row (priv->header_model,
1034 &(priv->next_row_reference), FALSE);
1035 modest_ui_actions_check_toolbar_dimming_rules (
1036 MODEST_WINDOW (window));
1040 /* Check if the newly inserted message is the same we are actually
1041 * showing. IF not, we should remain detached from the header model
1042 * and thus prev and next toolbarbuttons should remain dimmed. */
1043 gtk_tree_model_get (new_model, tree_iter,
1044 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1045 uid = modest_tny_folder_get_header_unique_id(header);
1046 g_object_unref(G_OBJECT(header));
1048 if(!g_str_equal(priv->msg_uid, uid)){
1054 /* Setup row_reference for the actual msg. */
1055 priv->row_reference = gtk_tree_row_reference_new(
1056 new_model, tree_path);
1057 if(priv->row_reference == NULL){
1058 g_warning("No reference for msg header item.");
1062 /* Attach new_model and connect some callback to it to become able
1063 * to detect changes in header-view. */
1064 priv->header_model = g_object_ref(new_model);
1065 g_signal_connect (new_model, "row-changed",
1066 G_CALLBACK (modest_msg_view_window_on_row_changed),
1068 g_signal_connect (new_model, "row-deleted",
1069 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1071 g_signal_connect (new_model, "rows-reordered",
1072 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1075 /* Now set up next_row_reference. */
1076 priv->next_row_reference = gtk_tree_row_reference_copy(
1077 priv->row_reference);
1078 select_next_valid_row (priv->header_model,
1079 &(priv->next_row_reference), FALSE);
1081 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1084 void modest_msg_view_window_on_row_reordered(
1085 GtkTreeModel *header_model,
1089 ModestMsgViewWindow *window){
1090 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1093 /* The modest_msg_view_window_update_model_replaced implements update
1094 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1095 * actually belongs to the header-view is the same as the TnyFolder of
1096 * the message of msg-view or not. If they are different, there is
1097 * nothing to do. If they are the same, then the model has replaced and
1098 * the reference in msg-view shall be replaced from the old model to
1099 * the new model. In this case the view will be detached from it's
1100 * header folder. From this point the next/prev buttons are dimmed.
1102 void modest_msg_view_window_update_model_replaced(
1103 ModestHeaderViewObserver *observer,
1104 GtkTreeModel *model,
1105 const gchar *tny_folder_id){
1106 ModestMsgViewWindowPrivate *priv = NULL;
1107 ModestMsgViewWindow *window = NULL;
1109 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1110 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1112 window = MODEST_MSG_VIEW_WINDOW(observer);
1113 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1115 /* If there is an other folder in the header-view then we do
1116 * not care about it's model (msg list). Else if the
1117 * header-view shows the folder the msg shown by us is in, we
1118 * shall replace our model reference and make some check. */
1119 if(tny_folder_id == NULL || !g_str_equal(tny_folder_id, priv->header_folder_id))
1122 /* Model is changed(replaced), so we should forget the old
1123 * one. Because there might be other references and there
1124 * might be some change on the model even if we unreferenced
1125 * it, we need to disconnect our signals here. */
1126 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1127 priv->row_changed_handler))
1128 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1129 priv->row_changed_handler);
1130 priv->row_changed_handler = 0;
1131 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1132 priv->row_deleted_handler))
1133 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1134 priv->row_deleted_handler);
1135 priv->row_deleted_handler = 0;
1136 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1137 priv->row_inserted_handler))
1138 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1139 priv->row_inserted_handler);
1140 priv->row_inserted_handler = 0;
1141 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1142 priv->rows_reordered_handler))
1143 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1144 priv->rows_reordered_handler);
1145 priv->rows_reordered_handler = 0;
1146 g_object_unref(priv->header_model);
1147 priv->header_model = NULL;
1148 g_object_unref(priv->row_reference);
1149 priv->row_reference = NULL;
1150 g_object_unref(priv->next_row_reference);
1151 priv->next_row_reference = NULL;
1153 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1155 g_assert(model != NULL);
1157 /* Also we must connect to the new model for row insertions.
1158 * Only for insertions now. We will need other ones only after
1159 * the msg is show by msg-view is added to the new model. */
1160 priv->row_inserted_handler = g_signal_connect (
1161 model, "row-inserted",
1162 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1167 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1169 ModestMsgViewWindowPrivate *priv= NULL;
1171 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1172 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1174 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1178 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1180 ModestMsgViewWindowPrivate *priv= NULL;
1182 TnyHeader *header = NULL;
1183 GtkTreePath *path = NULL;
1186 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1187 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1189 /* If the message was not obtained from a treemodel,
1190 * for instance if it was opened directly by the search UI:
1192 if (priv->header_model == NULL) {
1193 msg = modest_msg_view_window_get_message (self);
1195 header = tny_msg_get_header (msg);
1196 g_object_unref (msg);
1201 /* Get iter of the currently selected message in the header view: */
1202 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1203 * instead of sometimes retrieving it from the header view?
1204 * Then we wouldn't be dependent on the message actually still being selected
1205 * in the header view. murrayc. */
1206 if (!gtk_tree_row_reference_valid (priv->row_reference))
1208 path = gtk_tree_row_reference_get_path (priv->row_reference);
1209 g_return_val_if_fail (path != NULL, NULL);
1210 gtk_tree_model_get_iter (priv->header_model,
1214 /* Get current message header */
1215 gtk_tree_model_get (priv->header_model, &iter,
1216 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1219 gtk_tree_path_free (path);
1224 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1226 ModestMsgViewWindowPrivate *priv;
1228 g_return_val_if_fail (self, NULL);
1230 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1232 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1236 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1238 ModestMsgViewWindowPrivate *priv;
1240 g_return_val_if_fail (self, NULL);
1242 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1244 return (const gchar*) priv->msg_uid;
1248 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1251 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1252 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1253 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1257 is_active = gtk_toggle_action_get_active (toggle);
1260 gtk_widget_show (priv->find_toolbar);
1261 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1263 gtk_widget_hide (priv->find_toolbar);
1266 /* update the toggle buttons status */
1267 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1268 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1269 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1270 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1275 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1276 ModestMsgViewWindow *obj)
1278 GtkToggleAction *toggle;
1279 ModestWindowPrivate *parent_priv;
1280 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1282 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1283 gtk_toggle_action_set_active (toggle, FALSE);
1287 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1288 ModestMsgViewWindow *obj)
1290 gchar *current_search;
1291 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1293 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1294 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1298 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1300 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1301 g_free (current_search);
1302 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1306 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1308 g_free (priv->last_search);
1309 priv->last_search = g_strdup (current_search);
1310 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1313 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1314 g_free (priv->last_search);
1315 priv->last_search = NULL;
1317 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1320 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1321 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1322 g_free (priv->last_search);
1323 priv->last_search = NULL;
1325 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1329 g_free (current_search);
1334 modest_msg_view_window_set_zoom (ModestWindow *window,
1337 ModestMsgViewWindowPrivate *priv;
1338 ModestWindowPrivate *parent_priv;
1339 GtkAction *action = NULL;
1340 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1342 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1344 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1345 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1346 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1348 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1349 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1351 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1355 modest_msg_view_window_get_zoom (ModestWindow *window)
1357 ModestMsgViewWindowPrivate *priv;
1359 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1361 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1362 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1366 modest_msg_view_window_zoom_plus (ModestWindow *window)
1368 ModestWindowPrivate *parent_priv;
1369 GtkRadioAction *zoom_radio_action;
1370 GSList *group, *node;
1372 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1373 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1374 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1376 group = gtk_radio_action_get_group (zoom_radio_action);
1378 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1379 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1383 for (node = group; node != NULL; node = g_slist_next (node)) {
1384 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1385 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1393 modest_msg_view_window_zoom_minus (ModestWindow *window)
1395 ModestWindowPrivate *parent_priv;
1396 GtkRadioAction *zoom_radio_action;
1397 GSList *group, *node;
1399 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1400 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1401 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1403 group = gtk_radio_action_get_group (zoom_radio_action);
1405 for (node = group; node != NULL; node = g_slist_next (node)) {
1406 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1407 if (node->next != NULL) {
1408 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1411 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1421 modest_msg_view_window_key_event (GtkWidget *window,
1426 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1427 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1428 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1429 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1430 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1431 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1432 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1433 gboolean return_value;
1435 if (event->type == GDK_KEY_RELEASE) {
1436 GtkScrollType scroll_type;
1438 switch (event->keyval) {
1441 scroll_type = GTK_SCROLL_STEP_UP; break;
1444 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1446 case GDK_KP_Page_Up:
1447 scroll_type = GTK_SCROLL_PAGE_UP; break;
1449 case GDK_KP_Page_Down:
1450 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1453 scroll_type = GTK_SCROLL_START; break;
1456 scroll_type = GTK_SCROLL_END; break;
1457 default: scroll_type = GTK_SCROLL_NONE;
1460 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",
1461 scroll_type, FALSE, &return_value);
1472 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1475 ModestMsgViewWindowPrivate *priv;
1476 GtkTreeIter tmp_iter;
1477 gboolean is_last_selected;
1479 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1480 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1482 /*if no model (so no rows at all), then virtually we are the last*/
1483 if (!priv->header_model)
1486 path = gtk_tree_row_reference_get_path (priv->row_reference);
1490 is_last_selected = TRUE;
1491 while (is_last_selected) {
1493 gtk_tree_path_next (path);
1494 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1496 gtk_tree_model_get (priv->header_model, &tmp_iter,
1497 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1500 if (msg_is_visible (header, priv->is_outbox))
1501 is_last_selected = FALSE;
1502 g_object_unref(G_OBJECT(header));
1505 gtk_tree_path_free (path);
1506 return is_last_selected;
1510 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1512 ModestMsgViewWindowPrivate *priv;
1514 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1515 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1517 return priv->header_model != NULL;
1521 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1523 ModestMsgViewWindowPrivate *priv;
1525 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1526 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1528 return priv->is_search_result;
1532 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1534 return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1535 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1540 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1543 ModestMsgViewWindowPrivate *priv;
1544 gboolean is_first_selected;
1545 GtkTreeIter tmp_iter;
1546 /* gchar * path_string;*/
1548 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1549 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1551 /*if no model (so no rows at all), then virtually we are the first*/
1552 if (!priv->header_model)
1555 path = gtk_tree_row_reference_get_path (priv->row_reference);
1559 /* path_string = gtk_tree_path_to_string (path);
1560 is_first_selected = strcmp (path_string, "0");
1562 g_free (path_string);
1563 gtk_tree_path_free (path);
1565 return is_first_selected;*/
1567 is_first_selected = TRUE;
1568 while (is_first_selected) {
1570 if(!gtk_tree_path_prev (path))
1572 /* Here the 'if' is needless for logic, but let make sure
1573 * iter is valid for gtk_tree_model_get. */
1574 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1576 gtk_tree_model_get (priv->header_model, &tmp_iter,
1577 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1580 if (msg_is_visible (header, priv->is_outbox))
1581 is_first_selected = FALSE;
1582 g_object_unref(G_OBJECT(header));
1585 gtk_tree_path_free (path);
1586 return is_first_selected;
1590 * Reads the message whose summary item is @header. It takes care of
1591 * several things, among others:
1593 * If the message was not previously downloaded then ask the user
1594 * before downloading. If there is no connection launch the connection
1595 * dialog. Update toolbar dimming rules.
1597 * Returns: TRUE if the mail operation was started, otherwise if the
1598 * user do not want to download the message, or if the user do not
1599 * want to connect, then the operation is not issued
1602 message_reader (ModestMsgViewWindow *window,
1603 ModestMsgViewWindowPrivate *priv,
1605 GtkTreeRowReference *row_reference)
1607 ModestMailOperation *mail_op = NULL;
1608 gboolean already_showing = FALSE;
1609 ModestWindow *msg_window = NULL;
1610 ModestWindowMgr *mgr;
1612 g_return_val_if_fail (row_reference != NULL, FALSE);
1614 mgr = modest_runtime_get_window_mgr ();
1615 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1616 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1619 gtk_window_present (GTK_WINDOW (msg_window));
1620 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1624 /* Msg download completed */
1625 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1626 /* Ask the user if he wants to download the message if
1628 if (!tny_device_is_online (modest_runtime_get_device())) {
1629 TnyFolder *folder = NULL;
1630 GtkResponseType response;
1632 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1633 _("mcen_nc_get_msg"));
1634 if (response == GTK_RESPONSE_CANCEL)
1637 /* Offer the connection dialog if necessary */
1638 folder = tny_header_get_folder (header);
1640 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1641 TNY_FOLDER_STORE (folder))) {
1642 g_object_unref (folder);
1645 g_object_unref (folder);
1650 /* New mail operation */
1651 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(window),
1652 modest_ui_actions_get_msgs_full_error_handler,
1655 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1656 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, row_reference);
1657 g_object_unref (mail_op);
1659 /* Update toolbar dimming rules */
1660 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1666 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1668 ModestMsgViewWindowPrivate *priv;
1669 GtkTreePath *path= NULL;
1670 GtkTreeIter tmp_iter;
1672 gboolean retval = TRUE;
1673 GtkTreeRowReference *row_reference = NULL;
1675 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1676 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1678 /* Update the next row reference if it's not valid. This could
1679 happen if for example the header which it was pointing to,
1680 was deleted. The best place to do it is in the row-deleted
1681 handler but the tinymail model do not work like the glib
1682 tree models and reports the deletion when the row is still
1684 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1685 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1686 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1687 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1690 if (priv->next_row_reference)
1691 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1695 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1697 gtk_tree_model_get_iter (priv->header_model,
1700 gtk_tree_path_free (path);
1702 gtk_tree_model_get (priv->header_model, &tmp_iter,
1703 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1706 /* Read the message & show it */
1707 if (!message_reader (window, priv, header, row_reference)) {
1709 gtk_tree_row_reference_free (row_reference);
1713 g_object_unref (header);
1719 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1721 ModestMsgViewWindowPrivate *priv = NULL;
1722 TnyHeader *header = NULL;
1724 GtkTreePath *path = NULL;
1725 GtkTreeRowReference *row_reference = NULL;
1727 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1728 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1730 /* Check that the model is not empty */
1731 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1734 /* Get the header */
1735 gtk_tree_model_get (priv->header_model,
1737 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1739 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1740 if (!msg_is_visible (header, priv->is_outbox)) {
1741 g_object_unref (header);
1742 return modest_msg_view_window_select_next_message (self);
1745 path = gtk_tree_model_get_path (priv->header_model, &iter);
1746 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1747 gtk_tree_path_free (path);
1749 /* Read the message & show it */
1750 message_reader (self, priv, header, row_reference);
1753 g_object_unref (header);
1759 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1761 ModestMsgViewWindowPrivate *priv = NULL;
1763 GtkTreeRowReference *row_reference = NULL;
1765 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1766 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1768 /* Return inmediatly if there is no header model */
1769 if (!priv->header_model)
1772 path = gtk_tree_row_reference_get_path (priv->row_reference);
1773 while (gtk_tree_path_prev (path)) {
1777 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1778 gtk_tree_model_get (priv->header_model, &iter,
1779 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1783 if (!msg_is_visible (header, priv->is_outbox)) {
1784 g_object_unref (header);
1788 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1789 /* Read the message & show it */
1790 if (!message_reader (window, priv, header, row_reference)) {
1791 gtk_tree_row_reference_free (row_reference);
1792 g_object_unref (header);
1796 gtk_tree_path_free (path);
1797 g_object_unref (header);
1802 gtk_tree_path_free (path);
1807 view_msg_cb (ModestMailOperation *mail_op,
1814 ModestMsgViewWindow *self = NULL;
1815 ModestMsgViewWindowPrivate *priv = NULL;
1816 GtkTreeRowReference *row_reference = NULL;
1818 /* If there was any error */
1819 row_reference = (GtkTreeRowReference *) user_data;
1820 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1821 gtk_tree_row_reference_free (row_reference);
1825 /* Get the window */
1826 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1827 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1828 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1830 /* Update the row reference */
1831 gtk_tree_row_reference_free (priv->row_reference);
1832 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1833 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1834 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1835 gtk_tree_row_reference_free (row_reference);
1837 /* Mark header as read */
1838 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1839 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1841 /* Set new message */
1842 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1843 modest_msg_view_window_update_priority (self);
1844 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1845 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1847 /* Set the new message uid of the window */
1848 if (priv->msg_uid) {
1849 g_free (priv->msg_uid);
1850 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1853 /* Notify the observers */
1854 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1855 0, priv->header_model, priv->row_reference);
1857 /* Free new references */
1858 g_object_unref (self);
1862 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1864 ModestMsgViewWindowPrivate *priv;
1866 TnyFolderType folder_type;
1868 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1870 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1872 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1876 folder = tny_msg_get_folder (msg);
1878 folder_type = tny_folder_get_folder_type (folder);
1879 g_object_unref (folder);
1881 g_object_unref (msg);
1889 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1891 ModestMsgViewWindowPrivate *priv;
1892 TnyHeaderFlags flags = 0;
1894 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1896 if (priv->header_model) {
1899 GtkTreePath *path = NULL;
1901 path = gtk_tree_row_reference_get_path (priv->row_reference);
1902 g_return_if_fail (path != NULL);
1903 gtk_tree_model_get_iter (priv->header_model,
1905 gtk_tree_row_reference_get_path (priv->row_reference));
1907 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1910 flags = tny_header_get_flags (header);
1911 g_object_unref(G_OBJECT(header));
1913 gtk_tree_path_free (path);
1916 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1921 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1923 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1924 ModestWindowPrivate *parent_priv;
1925 ModestWindowMgr *mgr;
1926 gboolean is_fullscreen;
1927 GtkAction *fs_toggle_action;
1930 mgr = modest_runtime_get_window_mgr ();
1931 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1933 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1935 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1936 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1937 if (is_fullscreen != active) {
1938 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1947 set_homogeneous (GtkWidget *widget,
1950 if (GTK_IS_TOOL_ITEM (widget)) {
1951 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE);
1952 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE);
1957 modest_msg_view_window_show_toolbar (ModestWindow *self,
1958 gboolean show_toolbar)
1960 ModestMsgViewWindowPrivate *priv = NULL;
1961 ModestWindowPrivate *parent_priv;
1962 GtkWidget *reply_button = NULL, *menu = NULL;
1963 GtkWidget *placeholder = NULL;
1965 const gchar *action_name;
1968 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1969 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1971 /* Set optimized view status */
1972 priv->optimized_view = !show_toolbar;
1974 if (!parent_priv->toolbar) {
1975 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
1977 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
1979 /* Set homogeneous toolbar */
1980 gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar),
1981 set_homogeneous, NULL);
1983 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
1984 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
1985 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
1986 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
1987 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1988 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1989 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1990 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1992 /* Add ProgressBar (Transfer toolbar) */
1993 priv->progress_bar = modest_progress_bar_widget_new ();
1994 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
1995 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
1996 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
1997 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
1998 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2000 /* Connect cancel 'clicked' signal to abort progress mode */
2001 g_signal_connect(priv->cancel_toolitem, "clicked",
2002 G_CALLBACK(cancel_progressbar),
2005 /* Add it to the observers list */
2006 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2009 hildon_window_add_toolbar (HILDON_WINDOW (self),
2010 GTK_TOOLBAR (parent_priv->toolbar));
2012 /* Set reply button tap and hold menu */
2013 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2014 "/ToolBar/ToolbarMessageReply");
2015 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2016 "/ToolbarReplyCSM");
2017 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2021 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2022 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2023 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2025 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2026 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2027 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2029 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2032 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2033 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2036 /* Update also the actions (to update the toggles in the
2037 menus), we have to do it manually because some other window
2038 of the same time could have changed it (remember that the
2039 toolbar fullscreen mode is shared by all the windows of the
2041 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2042 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2044 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2046 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2047 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2052 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2054 ModestMsgViewWindow *window)
2056 if (!GTK_WIDGET_VISIBLE (window))
2059 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
2063 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2065 ModestMsgViewWindowPrivate *priv;
2067 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2068 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2070 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2074 cancel_progressbar (GtkToolButton *toolbutton,
2075 ModestMsgViewWindow *self)
2078 ModestMsgViewWindowPrivate *priv;
2080 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2082 /* Get operation observers and cancel its current operation */
2083 tmp = priv->progress_widgets;
2085 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2086 tmp=g_slist_next(tmp);
2090 observers_empty (ModestMsgViewWindow *self)
2093 ModestMsgViewWindowPrivate *priv;
2094 gboolean is_empty = TRUE;
2095 guint pending_ops = 0;
2097 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2098 tmp = priv->progress_widgets;
2100 /* Check all observers */
2101 while (tmp && is_empty) {
2102 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2103 is_empty = pending_ops == 0;
2105 tmp = g_slist_next(tmp);
2112 on_account_removed (TnyAccountStore *account_store,
2113 TnyAccount *account,
2116 /* Do nothing if it's a transport account, because we only
2117 show the messages of a store account */
2118 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2119 const gchar *parent_acc = NULL;
2120 const gchar *our_acc = NULL;
2122 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2123 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2125 /* Close this window if I'm showing a message of the removed account */
2126 if (strcmp (parent_acc, our_acc) == 0)
2127 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2132 on_mail_operation_started (ModestMailOperation *mail_op,
2135 ModestMsgViewWindow *self;
2136 ModestMailOperationTypeOperation op_type;
2138 ModestMsgViewWindowPrivate *priv;
2140 self = MODEST_MSG_VIEW_WINDOW (user_data);
2141 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2142 op_type = modest_mail_operation_get_type_operation (mail_op);
2143 tmp = priv->progress_widgets;
2145 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2146 set_toolbar_transfer_mode(self);
2148 modest_progress_object_add_operation (
2149 MODEST_PROGRESS_OBJECT (tmp->data),
2151 tmp = g_slist_next (tmp);
2157 on_mail_operation_finished (ModestMailOperation *mail_op,
2160 ModestMsgViewWindow *self;
2161 ModestMailOperationTypeOperation op_type;
2163 ModestMsgViewWindowPrivate *priv;
2165 self = MODEST_MSG_VIEW_WINDOW (user_data);
2166 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2167 op_type = modest_mail_operation_get_type_operation (mail_op);
2168 tmp = priv->progress_widgets;
2170 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2172 modest_progress_object_remove_operation (
2173 MODEST_PROGRESS_OBJECT (tmp->data),
2175 tmp = g_slist_next (tmp);
2178 /* If no more operations are being observed, NORMAL mode is enabled again */
2179 if (observers_empty (self)) {
2180 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2186 on_queue_changed (ModestMailOperationQueue *queue,
2187 ModestMailOperation *mail_op,
2188 ModestMailOperationQueueNotification type,
2189 ModestMsgViewWindow *self)
2191 ModestMsgViewWindowPrivate *priv;
2193 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2195 /* If this operations was created by another window, do nothing */
2196 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2199 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2200 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2202 "operation-started",
2203 G_CALLBACK (on_mail_operation_started),
2205 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2207 "operation-finished",
2208 G_CALLBACK (on_mail_operation_finished),
2210 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2211 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2213 "operation-started");
2214 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2216 "operation-finished");
2221 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2223 ModestMsgViewWindowPrivate *priv;
2224 GList *selected_attachments = NULL;
2226 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2227 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2229 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2231 return selected_attachments;
2235 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2237 ModestMsgViewWindowPrivate *priv;
2238 const gchar *msg_uid;
2239 gchar *attachment_uid = NULL;
2240 gint attachment_index = 0;
2243 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2244 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2245 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2247 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2248 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2249 attachment_index = g_list_index (attachments, mime_part);
2250 g_list_free (attachments);
2252 if (msg_uid && attachment_index >= 0) {
2253 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2256 if (mime_part == NULL) {
2257 gboolean error = FALSE;
2258 GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2259 if (selected_attachments == NULL) {
2261 } else if (g_list_length (selected_attachments) > 1) {
2262 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2265 mime_part = (TnyMimePart *) selected_attachments->data;
2266 g_object_ref (mime_part);
2268 g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL);
2269 g_list_free (selected_attachments);
2274 g_object_ref (mime_part);
2277 if (tny_mime_part_is_purged (mime_part)) {
2278 g_object_unref (mime_part);
2282 if (!TNY_IS_MSG (mime_part)) {
2283 gchar *filepath = NULL;
2284 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2285 const gchar *content_type;
2286 TnyFsStream *temp_stream = NULL;
2287 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2290 if (temp_stream != NULL) {
2291 content_type = tny_mime_part_get_content_type (mime_part);
2292 tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream));
2294 /* make the file read-only */
2295 if (g_chmod(filepath, 0444) != 0)
2296 g_warning ("%s: failed to set file '%s' to read-only: %s",
2297 __FUNCTION__, filepath, strerror(errno));
2299 modest_platform_activate_file (filepath, content_type);
2300 g_object_unref (temp_stream);
2302 /* NOTE: files in the temporary area will be automatically
2303 * cleaned after some time if they are no longer in use */
2304 } else if (filepath != NULL) {
2305 /* the file may already exist but it isn't writable,
2306 * let's try to open it anyway */
2307 content_type = tny_mime_part_get_content_type (mime_part);
2308 modest_platform_activate_file (filepath, content_type);
2312 /* message attachment */
2313 TnyHeader *header = NULL;
2314 ModestWindowMgr *mgr;
2315 ModestWindow *msg_win = NULL;
2318 header = tny_msg_get_header (TNY_MSG (mime_part));
2319 mgr = modest_runtime_get_window_mgr ();
2320 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2323 if (msg_win) /* there is already a window for this uid; top it */
2324 gtk_window_present (GTK_WINDOW(msg_win));
2326 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2327 * thus, we don't do anything */
2328 g_warning ("window for is already being created");
2330 /* it's not found, so create a new window for it */
2331 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2332 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2334 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2335 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2336 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2337 modest_window_get_zoom (MODEST_WINDOW (window)));
2338 modest_window_mgr_register_window (mgr, msg_win);
2339 gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window));
2340 gtk_widget_show_all (GTK_WIDGET (msg_win));
2343 g_object_unref (mime_part);
2359 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2360 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2361 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2362 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2365 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2369 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2370 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2371 g_free (pair->filename);
2372 g_object_unref (pair->part);
2373 g_slice_free (SaveMimePartPair, pair);
2375 g_list_free (info->pairs);
2378 gtk_widget_destroy (info->banner);
2379 g_object_unref (info->banner);
2380 g_slice_free (SaveMimePartInfo, info);
2385 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2387 if (info->pairs != NULL) {
2388 save_mime_part_to_file (info);
2391 result = info->result;
2393 /* This is a GDK lock because we are an idle callback and
2394 * hildon_banner_show_information is or does Gtk+ code */
2396 gdk_threads_enter (); /* CHECKED */
2397 save_mime_part_info_free (info, TRUE);
2399 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2401 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2403 gdk_threads_leave (); /* CHECKED */
2410 save_mime_part_to_file (SaveMimePartInfo *info)
2412 GnomeVFSResult result;
2413 GnomeVFSHandle *handle;
2415 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2417 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2418 if (result == GNOME_VFS_OK) {
2419 stream = tny_vfs_stream_new (handle);
2420 tny_mime_part_decode_to_stream (pair->part, stream);
2421 g_object_unref (G_OBJECT (stream));
2422 g_object_unref (pair->part);
2423 g_slice_free (SaveMimePartPair, pair);
2424 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2425 info->result = TRUE;
2427 save_mime_part_info_free (info, FALSE);
2428 info->result = FALSE;
2431 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2436 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2438 gboolean is_ok = TRUE;
2439 gint replaced_files = 0;
2440 const GList *files = info->pairs;
2443 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2444 SaveMimePartPair *pair = iter->data;
2445 if (modest_utils_file_exists (pair->filename)) {
2449 if (replaced_files) {
2450 GtkWidget *confirm_overwrite_dialog;
2451 const gchar *message = (replaced_files == 1) ?
2452 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2453 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2454 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2457 gtk_widget_destroy (confirm_overwrite_dialog);
2461 save_mime_part_info_free (info, TRUE);
2463 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2464 _CS("sfil_ib_saving"));
2465 info->banner = g_object_ref (banner);
2466 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2467 g_object_unref (banner);
2474 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts)
2476 gboolean clean_list = FALSE;
2477 ModestMsgViewWindowPrivate *priv;
2478 GList *files_to_save = NULL;
2479 GtkWidget *save_dialog = NULL;
2480 gchar *folder = NULL;
2481 gboolean canceled = FALSE;
2482 const gchar *filename = NULL;
2483 gchar *save_multiple_str = NULL;
2485 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2486 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2488 if (mime_parts == NULL) {
2489 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2490 if (mime_parts == NULL)
2495 /* prepare dialog */
2496 if (mime_parts->next == NULL) {
2497 /* only one attachment selected */
2498 TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data;
2499 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2500 filename = tny_mime_part_get_filename (mime_part);
2502 g_warning ("Tried to save a non-file attachment");
2506 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2507 g_list_length (mime_parts));
2510 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2511 GTK_FILE_CHOOSER_ACTION_SAVE);
2514 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2515 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2519 if (filename != NULL)
2520 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2523 /* if multiple, set multiple string */
2524 if (save_multiple_str) {
2525 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2529 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2530 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2532 if (!modest_utils_folder_writable (chooser_uri)) {
2533 hildon_banner_show_information
2534 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2538 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2539 TnyMimePart *mime_part = (TnyMimePart *) node->data;
2541 if (tny_mime_part_is_attachment (mime_part)) {
2542 SaveMimePartPair *pair;
2544 if ((mime_parts->next != NULL) &&
2545 (tny_mime_part_get_filename (mime_part) == NULL))
2548 pair = g_slice_new0 (SaveMimePartPair);
2549 if (mime_parts->next == NULL) {
2550 pair->filename = g_strdup (chooser_uri);
2553 g_build_filename (chooser_uri,
2554 tny_mime_part_get_filename (mime_part), NULL);
2556 pair->part = g_object_ref (mime_part);
2557 files_to_save = g_list_prepend (files_to_save, pair);
2561 g_free (chooser_uri);
2564 gtk_widget_destroy (save_dialog);
2567 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2568 g_list_free (mime_parts);
2571 if (files_to_save != NULL) {
2572 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2573 info->pairs = files_to_save;
2574 info->result = TRUE;
2575 save_mime_parts_to_file_with_checks (info);
2580 show_remove_attachment_information (gpointer userdata)
2582 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2583 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2585 /* We're outside the main lock */
2586 gdk_threads_enter ();
2588 if (priv->remove_attachment_banner != NULL) {
2589 gtk_widget_destroy (priv->remove_attachment_banner);
2590 g_object_unref (priv->remove_attachment_banner);
2593 priv->remove_attachment_banner = g_object_ref (
2594 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2596 gdk_threads_leave ();
2602 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2604 ModestMsgViewWindowPrivate *priv;
2605 GList *mime_parts = NULL, *node;
2606 gchar *confirmation_message;
2610 /* TnyFolder *folder; */
2612 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2613 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2616 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2618 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2620 /* Remove already purged messages from mime parts list */
2622 while (node != NULL) {
2623 TnyMimePart *part = TNY_MIME_PART (node->data);
2624 if (tny_mime_part_is_purged (part)) {
2625 GList *deleted_node = node;
2626 node = g_list_next (node);
2627 g_object_unref (part);
2628 mime_parts = g_list_delete_link (mime_parts, deleted_node);
2630 node = g_list_next (node);
2634 if (mime_parts == NULL)
2637 n_attachments = g_list_length (mime_parts);
2638 if (n_attachments == 1) {
2639 const gchar *filename;
2641 if (TNY_IS_MSG (mime_parts->data)) {
2643 header = tny_msg_get_header (TNY_MSG (mime_parts->data));
2644 filename = tny_header_get_subject (header);
2645 g_object_unref (header);
2646 if (filename == NULL)
2647 filename = _("mail_va_no_subject");
2649 filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data));
2651 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2653 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2654 "mcen_nc_purge_files_text",
2655 n_attachments), n_attachments);
2657 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2658 confirmation_message);
2659 g_free (confirmation_message);
2661 if (response != GTK_RESPONSE_OK)
2664 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2665 /* folder = tny_msg_get_folder (msg); */
2666 /* tny_msg_uncache_attachments (msg); */
2667 /* tny_folder_refresh (folder, NULL); */
2668 /* g_object_unref (folder); */
2670 for (node = mime_parts; node != NULL; node = g_list_next (node)) {
2671 tny_mime_part_set_purged (TNY_MIME_PART (node->data));
2672 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2675 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2676 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2677 tny_msg_rewrite_cache (msg);
2678 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2680 g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL);
2681 g_list_free (mime_parts);
2683 if (priv->purge_timeout > 0) {
2684 g_source_remove (priv->purge_timeout);
2685 priv->purge_timeout = 0;
2688 if (priv->remove_attachment_banner) {
2689 gtk_widget_destroy (priv->remove_attachment_banner);
2690 g_object_unref (priv->remove_attachment_banner);
2691 priv->remove_attachment_banner = NULL;
2699 update_window_title (ModestMsgViewWindow *window)
2701 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2703 TnyHeader *header = NULL;
2704 const gchar *subject = NULL;
2706 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2709 header = tny_msg_get_header (msg);
2710 subject = tny_header_get_subject (header);
2711 g_object_unref (msg);
2714 if ((subject == NULL)||(subject[0] == '\0'))
2715 subject = _("mail_va_no_subject");
2717 gtk_window_set_title (GTK_WINDOW (window), subject);
2721 static void on_move_focus (GtkWidget *widget,
2722 GtkDirectionType direction,
2725 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");