1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar.h"
51 #include "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include "modest-ui-dimming-manager.h"
54 #include <gdk/gdkkeysyms.h>
55 #include <modest-tny-account.h>
56 #include <modest-mime-part-view.h>
57 #include <modest-isearch-view.h>
58 #include <modest-tny-mime-part.h>
61 #include <glib/gstdio.h>
62 #include <modest-debug.h>
64 #define DEFAULT_FOLDER "MyDocs/.documents"
66 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
67 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
68 static void modest_header_view_observer_init(
69 ModestHeaderViewObserverIface *iface_class);
70 static void modest_msg_view_window_finalize (GObject *obj);
71 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
73 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
74 ModestMsgViewWindow *obj);
75 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
76 ModestMsgViewWindow *obj);
78 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
79 static void modest_msg_view_window_set_zoom (ModestWindow *window,
81 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
82 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
83 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
84 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
87 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
88 GdkEventWindowState *event,
90 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
92 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
93 gboolean show_toolbar);
95 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
97 ModestMsgViewWindow *window);
99 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
102 ModestMsgViewWindow *window);
104 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
106 ModestMsgViewWindow *window);
108 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
109 GtkTreePath *tree_path,
110 GtkTreeIter *tree_iter,
111 ModestMsgViewWindow *window);
113 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
117 ModestMsgViewWindow *window);
119 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
121 const gchar *tny_folder_id);
123 static void cancel_progressbar (GtkToolButton *toolbutton,
124 ModestMsgViewWindow *self);
126 static void on_queue_changed (ModestMailOperationQueue *queue,
127 ModestMailOperation *mail_op,
128 ModestMailOperationQueueNotification type,
129 ModestMsgViewWindow *self);
131 static void on_account_removed (TnyAccountStore *account_store,
135 static void on_move_focus (GtkWidget *widget,
136 GtkDirectionType direction,
139 static void view_msg_cb (ModestMailOperation *mail_op,
146 static void set_toolbar_mode (ModestMsgViewWindow *self,
147 ModestToolBarModes mode);
149 static void update_window_title (ModestMsgViewWindow *window);
151 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
152 static void init_window (ModestMsgViewWindow *obj);
154 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
156 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
158 /* list my signals */
164 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
165 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
166 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
169 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
170 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
171 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
172 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
173 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
174 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
175 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
178 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
179 struct _ModestMsgViewWindowPrivate {
182 GtkWidget *main_scroll;
183 GtkWidget *find_toolbar;
186 /* Progress observers */
187 GtkWidget *progress_bar;
188 GSList *progress_widgets;
191 GtkWidget *progress_toolitem;
192 GtkWidget *cancel_toolitem;
193 GtkWidget *prev_toolitem;
194 GtkWidget *next_toolitem;
195 ModestToolBarModes current_toolbar_mode;
197 /* Optimized view enabled */
198 gboolean optimized_view;
200 /* Whether this was created via the *_new_for_search_result() function. */
201 gboolean is_search_result;
203 /* Whether the message is in outbox */
206 /* A reference to the @model of the header view
207 * to allow selecting previous/next messages,
208 * if the message is currently selected in the header view.
210 const gchar *header_folder_id;
211 GtkTreeModel *header_model;
212 GtkTreeRowReference *row_reference;
213 GtkTreeRowReference *next_row_reference;
215 gulong clipboard_change_handler;
216 gulong queue_change_handler;
217 gulong account_removed_handler;
218 gulong row_changed_handler;
219 gulong row_deleted_handler;
220 gulong row_inserted_handler;
221 gulong rows_reordered_handler;
224 GtkWidget *remove_attachment_banner;
226 guint progress_bar_timeout;
233 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
234 MODEST_TYPE_MSG_VIEW_WINDOW, \
235 ModestMsgViewWindowPrivate))
237 static GtkWindowClass *parent_class = NULL;
239 /* uncomment the following if you have defined any signals */
240 static guint signals[LAST_SIGNAL] = {0};
243 modest_msg_view_window_get_type (void)
245 static GType my_type = 0;
247 static const GTypeInfo my_info = {
248 sizeof(ModestMsgViewWindowClass),
249 NULL, /* base init */
250 NULL, /* base finalize */
251 (GClassInitFunc) modest_msg_view_window_class_init,
252 NULL, /* class finalize */
253 NULL, /* class data */
254 sizeof(ModestMsgViewWindow),
256 (GInstanceInitFunc) modest_msg_view_window_init,
259 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
260 "ModestMsgViewWindow",
263 static const GInterfaceInfo modest_header_view_observer_info =
265 (GInterfaceInitFunc) modest_header_view_observer_init,
266 NULL, /* interface_finalize */
267 NULL /* interface_data */
270 g_type_add_interface_static (my_type,
271 MODEST_TYPE_HEADER_VIEW_OBSERVER,
272 &modest_header_view_observer_info);
278 save_state (ModestWindow *self)
280 modest_widget_memory_save (modest_runtime_get_conf (),
282 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
287 restore_settings (ModestMsgViewWindow *self)
290 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
293 conf = modest_runtime_get_conf ();
294 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
295 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
296 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
297 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
298 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
299 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
300 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
301 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
302 modest_widget_memory_restore (conf,
304 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
308 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
310 GObjectClass *gobject_class;
311 ModestWindowClass *modest_window_class;
312 gobject_class = (GObjectClass*) klass;
313 modest_window_class = (ModestWindowClass *) klass;
315 parent_class = g_type_class_peek_parent (klass);
316 gobject_class->finalize = modest_msg_view_window_finalize;
318 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
319 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
320 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
321 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
322 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
323 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
325 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
327 modest_window_class->save_state_func = save_state;
329 signals[MSG_CHANGED_SIGNAL] =
330 g_signal_new ("msg-changed",
331 G_TYPE_FROM_CLASS (gobject_class),
333 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
335 modest_marshal_VOID__POINTER_POINTER,
336 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
339 static void modest_header_view_observer_init(
340 ModestHeaderViewObserverIface *iface_class)
342 iface_class->update_func = modest_msg_view_window_update_model_replaced;
346 modest_msg_view_window_init (ModestMsgViewWindow *obj)
348 ModestMsgViewWindowPrivate *priv;
349 ModestWindowPrivate *parent_priv = NULL;
350 GtkActionGroup *action_group = NULL;
351 GError *error = NULL;
352 GdkPixbuf *window_icon;
354 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
355 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
356 parent_priv->ui_manager = gtk_ui_manager_new();
358 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
359 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
361 /* Add common actions */
362 gtk_action_group_add_actions (action_group,
363 modest_action_entries,
364 G_N_ELEMENTS (modest_action_entries),
366 gtk_action_group_add_toggle_actions (action_group,
367 modest_toggle_action_entries,
368 G_N_ELEMENTS (modest_toggle_action_entries),
370 gtk_action_group_add_toggle_actions (action_group,
371 msg_view_toggle_action_entries,
372 G_N_ELEMENTS (msg_view_toggle_action_entries),
374 gtk_action_group_add_radio_actions (action_group,
375 msg_view_zoom_action_entries,
376 G_N_ELEMENTS (msg_view_zoom_action_entries),
378 G_CALLBACK (modest_ui_actions_on_change_zoom),
381 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
382 g_object_unref (action_group);
384 /* Load the UI definition */
385 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
388 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
389 g_error_free (error);
394 /* Add accelerators */
395 gtk_window_add_accel_group (GTK_WINDOW (obj),
396 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
398 priv->is_search_result = FALSE;
399 priv->is_outbox = FALSE;
401 priv->msg_view = NULL;
402 priv->header_model = NULL;
403 priv->header_folder_id = NULL;
404 priv->clipboard_change_handler = 0;
405 priv->queue_change_handler = 0;
406 priv->account_removed_handler = 0;
407 priv->row_changed_handler = 0;
408 priv->row_deleted_handler = 0;
409 priv->row_inserted_handler = 0;
410 priv->rows_reordered_handler = 0;
411 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
413 priv->optimized_view = FALSE;
414 priv->progress_bar_timeout = 0;
415 priv->purge_timeout = 0;
416 priv->remove_attachment_banner = NULL;
417 priv->msg_uid = NULL;
419 priv->sighandlers = NULL;
422 init_window (MODEST_MSG_VIEW_WINDOW(obj));
424 /* Set window icon */
425 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
427 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
428 g_object_unref (window_icon);
431 hildon_program_add_window (hildon_program_get_instance(),
434 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
435 GTK_WINDOW(obj),"applications_email_viewer");
440 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
442 ModestMsgViewWindowPrivate *priv = NULL;
444 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
446 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
448 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
450 if (priv->progress_bar_timeout > 0) {
451 g_source_remove (priv->progress_bar_timeout);
452 priv->progress_bar_timeout = 0;
459 set_toolbar_mode (ModestMsgViewWindow *self,
460 ModestToolBarModes mode)
462 ModestWindowPrivate *parent_priv;
463 ModestMsgViewWindowPrivate *priv;
464 /* GtkWidget *widget = NULL; */
466 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
468 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
469 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
471 /* Sets current toolbar mode */
472 priv->current_toolbar_mode = mode;
474 /* Update toolbar dimming state */
475 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
478 case TOOLBAR_MODE_NORMAL:
479 if (priv->progress_toolitem) {
480 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
481 gtk_widget_hide (priv->progress_toolitem);
484 if (priv->progress_bar)
485 gtk_widget_hide (priv->progress_bar);
487 if (priv->cancel_toolitem)
488 gtk_widget_hide (priv->cancel_toolitem);
490 if (priv->prev_toolitem)
491 gtk_widget_show (priv->prev_toolitem);
493 if (priv->next_toolitem)
494 gtk_widget_show (priv->next_toolitem);
496 /* Hide toolbar if optimized view is enabled */
497 if (priv->optimized_view) {
498 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
499 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
503 case TOOLBAR_MODE_TRANSFER:
504 if (priv->prev_toolitem)
505 gtk_widget_hide (priv->prev_toolitem);
507 if (priv->next_toolitem)
508 gtk_widget_hide (priv->next_toolitem);
510 if (priv->progress_bar)
511 gtk_widget_show (priv->progress_bar);
513 if (priv->progress_toolitem) {
514 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
515 gtk_widget_show (priv->progress_toolitem);
518 if (priv->cancel_toolitem)
519 gtk_widget_show (priv->cancel_toolitem);
521 /* Show toolbar if it's hiden (optimized view ) */
522 if (priv->optimized_view) {
523 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
524 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
529 g_return_if_reached ();
536 init_window (ModestMsgViewWindow *obj)
538 GtkWidget *main_vbox;
539 ModestMsgViewWindowPrivate *priv;
541 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
543 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
544 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
545 main_vbox = gtk_vbox_new (FALSE, 6);
547 #ifdef MODEST_USE_MOZEMBED
548 priv->main_scroll = priv->msg_view;
549 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
551 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
552 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
554 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
555 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
556 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
558 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
559 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
561 priv->find_toolbar = hildon_find_toolbar_new (NULL);
562 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
563 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
565 gtk_widget_show_all (GTK_WIDGET(main_vbox));
569 modest_msg_view_window_disconnect_signals (ModestWindow *self)
571 ModestMsgViewWindowPrivate *priv;
572 ModestHeaderView *header_view = NULL;
573 ModestWindow *main_window = NULL;
575 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
577 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
578 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
579 priv->clipboard_change_handler))
580 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
581 priv->clipboard_change_handler);
583 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
584 priv->queue_change_handler))
585 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
586 priv->queue_change_handler);
588 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
589 priv->account_removed_handler))
590 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
591 priv->account_removed_handler);
593 if (priv->header_model) {
594 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
595 priv->row_changed_handler))
596 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
597 priv->row_changed_handler);
599 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
600 priv->row_deleted_handler))
601 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
602 priv->row_deleted_handler);
604 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
605 priv->row_inserted_handler))
606 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
607 priv->row_inserted_handler);
609 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
610 priv->rows_reordered_handler))
611 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
612 priv->rows_reordered_handler);
615 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
616 priv->sighandlers = NULL;
618 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
619 FALSE); /* don't create */
623 header_view = MODEST_HEADER_VIEW(
624 modest_main_window_get_child_widget(
625 MODEST_MAIN_WINDOW(main_window),
626 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
627 if (header_view == NULL)
630 modest_header_view_remove_observer(header_view,
631 MODEST_HEADER_VIEW_OBSERVER(self));
635 modest_msg_view_window_finalize (GObject *obj)
637 ModestMsgViewWindowPrivate *priv;
639 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
641 /* Sanity check: shouldn't be needed, the window mgr should
642 call this function before */
643 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
645 if (priv->header_model != NULL) {
646 g_object_unref (priv->header_model);
647 priv->header_model = NULL;
650 if (priv->progress_bar_timeout > 0) {
651 g_source_remove (priv->progress_bar_timeout);
652 priv->progress_bar_timeout = 0;
655 if (priv->remove_attachment_banner) {
656 gtk_widget_destroy (priv->remove_attachment_banner);
657 g_object_unref (priv->remove_attachment_banner);
658 priv->remove_attachment_banner = NULL;
661 if (priv->purge_timeout > 0) {
662 g_source_remove (priv->purge_timeout);
663 priv->purge_timeout = 0;
666 if (priv->row_reference) {
667 gtk_tree_row_reference_free (priv->row_reference);
668 priv->row_reference = NULL;
671 if (priv->next_row_reference) {
672 gtk_tree_row_reference_free (priv->next_row_reference);
673 priv->next_row_reference = NULL;
677 g_free (priv->msg_uid);
678 priv->msg_uid = NULL;
681 G_OBJECT_CLASS(parent_class)->finalize (obj);
685 select_next_valid_row (GtkTreeModel *model,
686 GtkTreeRowReference **row_reference,
690 GtkTreeIter tmp_iter;
692 GtkTreePath *next = NULL;
693 gboolean retval = FALSE, finished;
695 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
697 path = gtk_tree_row_reference_get_path (*row_reference);
698 gtk_tree_model_get_iter (model, &tmp_iter, path);
699 gtk_tree_row_reference_free (*row_reference);
700 *row_reference = NULL;
704 TnyHeader *header = NULL;
706 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
707 gtk_tree_model_get (model, &tmp_iter,
708 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
712 if (msg_is_visible (header, is_outbox)) {
713 next = gtk_tree_model_get_path (model, &tmp_iter);
714 *row_reference = gtk_tree_row_reference_new (model, next);
718 g_object_unref (header);
721 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
722 next = gtk_tree_model_get_path (model, &tmp_iter);
724 /* Ensure that we are not selecting the same */
725 if (gtk_tree_path_compare (path, next) != 0) {
726 gtk_tree_model_get (model, &tmp_iter,
727 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
730 if (msg_is_visible (header, is_outbox)) {
731 *row_reference = gtk_tree_row_reference_new (model, next);
735 g_object_unref (header);
739 /* If we ended up in the same message
740 then there is no valid next
745 /* If there are no more messages and we don't
746 want to start again in the first one then
747 there is no valid next message */
753 gtk_tree_path_free (path);
755 gtk_tree_path_free (next);
760 /* TODO: This should be in _init(), with the parameters as properties. */
762 modest_msg_view_window_construct (ModestMsgViewWindow *self,
763 const gchar *modest_account_name,
764 const gchar *msg_uid)
767 ModestMsgViewWindowPrivate *priv = NULL;
768 ModestWindowPrivate *parent_priv = NULL;
769 ModestDimmingRulesGroup *menu_rules_group = NULL;
770 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
771 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
773 obj = G_OBJECT (self);
774 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
775 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
777 priv->msg_uid = g_strdup (msg_uid);
780 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
781 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
782 gtk_widget_show (parent_priv->menubar);
783 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
785 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
786 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
787 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
789 /* Add common dimming rules */
790 modest_dimming_rules_group_add_rules (menu_rules_group,
791 modest_msg_view_menu_dimming_entries,
792 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
793 MODEST_WINDOW (self));
794 modest_dimming_rules_group_add_rules (toolbar_rules_group,
795 modest_msg_view_toolbar_dimming_entries,
796 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
797 MODEST_WINDOW (self));
798 modest_dimming_rules_group_add_rules (clipboard_rules_group,
799 modest_msg_view_clipboard_dimming_entries,
800 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
801 MODEST_WINDOW (self));
803 /* Insert dimming rules group for this window */
804 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
805 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
806 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
807 g_object_unref (menu_rules_group);
808 g_object_unref (toolbar_rules_group);
809 g_object_unref (clipboard_rules_group);
811 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
813 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
815 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);
816 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
817 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
818 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
819 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
820 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
821 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
822 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
823 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
824 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
825 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
827 g_signal_connect (G_OBJECT (obj), "key-release-event",
828 G_CALLBACK (modest_msg_view_window_key_event),
831 g_signal_connect (G_OBJECT (obj), "key-press-event",
832 G_CALLBACK (modest_msg_view_window_key_event),
835 g_signal_connect (G_OBJECT (obj), "window-state-event",
836 G_CALLBACK (modest_msg_view_window_window_state_event),
839 g_signal_connect (G_OBJECT (obj), "move-focus",
840 G_CALLBACK (on_move_focus), obj);
842 /* Mail Operation Queue */
843 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
845 G_CALLBACK (on_queue_changed),
848 /* Account manager */
849 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
851 G_CALLBACK(on_account_removed),
854 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
856 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
857 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
858 priv->last_search = NULL;
860 /* Init the clipboard actions dim status */
861 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
863 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
868 /* FIXME: parameter checks */
870 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
871 const gchar *modest_account_name,
872 const gchar *msg_uid,
874 GtkTreeRowReference *row_reference)
876 ModestMsgViewWindow *window = NULL;
877 ModestMsgViewWindowPrivate *priv = NULL;
878 TnyFolder *header_folder = NULL;
879 ModestHeaderView *header_view = NULL;
880 ModestWindow *main_window = NULL;
881 ModestWindowMgr *mgr = NULL;
884 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
887 mgr = modest_runtime_get_window_mgr ();
888 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
889 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
891 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
893 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
895 /* Remember the message list's TreeModel so we can detect changes
896 * and change the list selection when necessary: */
898 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
900 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
901 MODEST_MAIN_WINDOW(main_window),
902 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
905 if (header_view != NULL){
906 header_folder = modest_header_view_get_folder(header_view);
907 /* This could happen if the header folder was
908 unseleted before opening this msg window (for
909 example if the user selects an account in the
910 folder view of the main window */
912 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
913 priv->header_folder_id = tny_folder_get_id(header_folder);
914 g_assert(priv->header_folder_id != NULL);
915 g_object_unref(header_folder);
919 /* Setup row references and connect signals */
920 priv->header_model = g_object_ref (model);
923 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
924 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
925 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
927 priv->row_reference = NULL;
928 priv->next_row_reference = NULL;
931 /* Connect signals */
932 priv->row_changed_handler =
933 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
934 G_CALLBACK(modest_msg_view_window_on_row_changed),
936 priv->row_deleted_handler =
937 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
938 G_CALLBACK(modest_msg_view_window_on_row_deleted),
940 priv->row_inserted_handler =
941 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
942 G_CALLBACK(modest_msg_view_window_on_row_inserted),
944 priv->rows_reordered_handler =
945 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
946 G_CALLBACK(modest_msg_view_window_on_row_reordered),
949 if (header_view != NULL){
950 modest_header_view_add_observer(header_view,
951 MODEST_HEADER_VIEW_OBSERVER(window));
954 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
955 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
956 gtk_widget_show_all (GTK_WIDGET (window));
957 modest_msg_view_window_update_priority (window);
959 /* Check dimming rules */
960 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
961 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
962 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
964 return MODEST_WINDOW(window);
968 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
969 const gchar *modest_account_name,
970 const gchar *msg_uid)
972 ModestMsgViewWindow *window = NULL;
973 ModestMsgViewWindowPrivate *priv = NULL;
974 ModestWindowMgr *mgr = NULL;
976 mgr = modest_runtime_get_window_mgr ();
977 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
978 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
979 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
981 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
983 /* Remember that this is a search result,
984 * so we can disable some UI appropriately: */
985 priv->is_search_result = TRUE;
987 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
989 update_window_title (window);
990 gtk_widget_show_all (GTK_WIDGET (window));
991 modest_msg_view_window_update_priority (window);
993 /* Check dimming rules */
994 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
995 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
996 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
998 return MODEST_WINDOW(window);
1002 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1003 const gchar *modest_account_name,
1004 const gchar *msg_uid)
1006 GObject *obj = NULL;
1007 ModestMsgViewWindowPrivate *priv;
1008 ModestWindowMgr *mgr = NULL;
1010 g_return_val_if_fail (msg, NULL);
1011 mgr = modest_runtime_get_window_mgr ();
1012 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1013 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1014 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1015 modest_account_name, msg_uid);
1017 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1018 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1020 gtk_widget_show_all (GTK_WIDGET (obj));
1022 /* Check dimming rules */
1023 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1024 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1025 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1027 return MODEST_WINDOW(obj);
1031 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1034 ModestMsgViewWindow *window)
1036 check_dimming_rules_after_change (window);
1040 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1042 ModestMsgViewWindow *window)
1044 check_dimming_rules_after_change (window);
1048 check_dimming_rules_after_change_in_idle (gpointer data)
1050 /* The window could have dissapeared */
1051 if (MODEST_IS_WINDOW (data)) {
1052 ModestWindow *win = MODEST_WINDOW (data);
1053 gdk_threads_enter ();
1054 modest_ui_actions_check_menu_dimming_rules (win);
1055 modest_ui_actions_check_toolbar_dimming_rules (win);
1056 gdk_threads_leave ();
1063 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1065 static guint dimming_delayer = 0;
1067 if (dimming_delayer > 0)
1068 g_source_remove (dimming_delayer);
1070 /* We're expecting a lot of changes at the same time so don't
1071 need to check dimming rules for every change that
1073 dimming_delayer = g_timeout_add (100, check_dimming_rules_after_change_in_idle, window);
1077 /* On insertions we check if the folder still has the message we are
1078 * showing or do not. If do not, we do nothing. Which means we are still
1079 * not attached to any header folder and thus next/prev buttons are
1080 * still dimmed. Once the message that is shown by msg-view is found, the
1081 * new model of header-view will be attached and the references will be set.
1082 * On each further insertions dimming rules will be checked. However
1083 * this requires extra CPU time at least works.
1084 * (An message might be deleted from TnyFolder and thus will not be
1085 * inserted into the model again for example if it is removed by the
1086 * imap server and the header view is refreshed.)
1089 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1090 GtkTreePath *tree_path,
1091 GtkTreeIter *tree_iter,
1092 ModestMsgViewWindow *window)
1094 ModestMsgViewWindowPrivate *priv = NULL;
1095 TnyHeader *header = NULL;
1097 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1098 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1100 g_assert (model == priv->header_model);
1102 /* Check if the newly inserted message is the same we are actually
1103 * showing. IF not, we should remain detached from the header model
1104 * and thus prev and next toolbar buttons should remain dimmed. */
1105 gtk_tree_model_get (model, tree_iter,
1106 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1109 if (TNY_IS_HEADER (header)) {
1112 uid = modest_tny_folder_get_header_unique_id (header);
1113 if (!g_str_equal(priv->msg_uid, uid)) {
1114 check_dimming_rules_after_change (window);
1116 g_object_unref (G_OBJECT(header));
1120 g_object_unref(G_OBJECT(header));
1123 if (priv->row_reference) {
1124 gtk_tree_row_reference_free (priv->row_reference);
1127 /* Setup row_reference for the actual msg. */
1128 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1129 if (priv->row_reference == NULL) {
1130 g_warning("No reference for msg header item.");
1134 /* Now set up next_row_reference. */
1135 if (priv->next_row_reference) {
1136 gtk_tree_row_reference_free (priv->next_row_reference);
1139 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1140 select_next_valid_row (priv->header_model,
1141 &(priv->next_row_reference), FALSE, priv->is_outbox);
1143 /* Connect the remaining callbacks to become able to detect
1144 * changes in header-view. */
1145 priv->row_changed_handler =
1146 g_signal_connect (priv->header_model, "row-changed",
1147 G_CALLBACK (modest_msg_view_window_on_row_changed),
1149 priv->row_deleted_handler =
1150 g_signal_connect (priv->header_model, "row-deleted",
1151 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1153 priv->rows_reordered_handler =
1154 g_signal_connect (priv->header_model, "rows-reordered",
1155 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1158 check_dimming_rules_after_change (window);
1162 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1166 ModestMsgViewWindow *window)
1168 ModestMsgViewWindowPrivate *priv = NULL;
1169 gboolean already_changed = FALSE;
1171 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1173 /* If the current row was reordered select the proper next
1174 valid row. The same if the next row reference changes */
1175 if (priv->row_reference &&
1176 gtk_tree_row_reference_valid (priv->row_reference)) {
1178 path = gtk_tree_row_reference_get_path (priv->row_reference);
1179 if (gtk_tree_path_compare (path, arg1) == 0) {
1180 if (priv->next_row_reference) {
1181 gtk_tree_row_reference_free (priv->next_row_reference);
1183 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1184 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1185 already_changed = TRUE;
1187 gtk_tree_path_free (path);
1189 if (!already_changed &&
1190 priv->next_row_reference &&
1191 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1193 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1194 if (gtk_tree_path_compare (path, arg1) == 0) {
1195 if (priv->next_row_reference) {
1196 gtk_tree_row_reference_free (priv->next_row_reference);
1198 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1199 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1201 gtk_tree_path_free (path);
1203 check_dimming_rules_after_change (window);
1206 /* The modest_msg_view_window_update_model_replaced implements update
1207 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1208 * actually belongs to the header-view is the same as the TnyFolder of
1209 * the message of msg-view or not. If they are different, there is
1210 * nothing to do. If they are the same, then the model has replaced and
1211 * the reference in msg-view shall be replaced from the old model to
1212 * the new model. In this case the view will be detached from it's
1213 * header folder. From this point the next/prev buttons are dimmed.
1216 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1217 GtkTreeModel *model,
1218 const gchar *tny_folder_id)
1220 ModestMsgViewWindowPrivate *priv = NULL;
1221 ModestMsgViewWindow *window = NULL;
1223 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1224 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1226 window = MODEST_MSG_VIEW_WINDOW(observer);
1227 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1229 /* If there is an other folder in the header-view then we do
1230 * not care about it's model (msg list). Else if the
1231 * header-view shows the folder the msg shown by us is in, we
1232 * shall replace our model reference and make some check. */
1233 if(model == NULL || tny_folder_id == NULL ||
1234 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1237 /* Model is changed(replaced), so we should forget the old
1238 * one. Because there might be other references and there
1239 * might be some change on the model even if we unreferenced
1240 * it, we need to disconnect our signals here. */
1241 if (priv->header_model) {
1242 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1243 priv->row_changed_handler))
1244 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1245 priv->row_changed_handler);
1246 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1247 priv->row_deleted_handler))
1248 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1249 priv->row_deleted_handler);
1250 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1251 priv->row_inserted_handler))
1252 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1253 priv->row_inserted_handler);
1254 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1255 priv->rows_reordered_handler))
1256 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1257 priv->rows_reordered_handler);
1260 if (priv->row_reference)
1261 gtk_tree_row_reference_free (priv->row_reference);
1262 if (priv->next_row_reference)
1263 gtk_tree_row_reference_free (priv->next_row_reference);
1264 g_object_unref(priv->header_model);
1267 priv->row_changed_handler = 0;
1268 priv->row_deleted_handler = 0;
1269 priv->row_inserted_handler = 0;
1270 priv->rows_reordered_handler = 0;
1271 priv->next_row_reference = NULL;
1272 priv->row_reference = NULL;
1273 priv->header_model = NULL;
1276 priv->header_model = g_object_ref (model);
1278 /* Also we must connect to the new model for row insertions.
1279 * Only for insertions now. We will need other ones only after
1280 * the msg is show by msg-view is added to the new model. */
1281 priv->row_inserted_handler =
1282 g_signal_connect (priv->header_model, "row-inserted",
1283 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1286 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1287 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1291 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1293 ModestMsgViewWindowPrivate *priv= NULL;
1295 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1296 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1298 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1302 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1304 ModestMsgViewWindowPrivate *priv= NULL;
1306 TnyHeader *header = NULL;
1307 GtkTreePath *path = NULL;
1310 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1311 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1313 /* If the message was not obtained from a treemodel,
1314 * for instance if it was opened directly by the search UI:
1316 if (priv->header_model == NULL ||
1317 priv->row_reference == NULL ||
1318 !gtk_tree_row_reference_valid (priv->row_reference)) {
1319 msg = modest_msg_view_window_get_message (self);
1321 header = tny_msg_get_header (msg);
1322 g_object_unref (msg);
1327 /* Get iter of the currently selected message in the header view: */
1328 path = gtk_tree_row_reference_get_path (priv->row_reference);
1329 g_return_val_if_fail (path != NULL, NULL);
1330 gtk_tree_model_get_iter (priv->header_model,
1334 /* Get current message header */
1335 gtk_tree_model_get (priv->header_model, &iter,
1336 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1339 gtk_tree_path_free (path);
1344 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1346 ModestMsgViewWindowPrivate *priv;
1348 g_return_val_if_fail (self, NULL);
1350 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1352 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1356 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1358 ModestMsgViewWindowPrivate *priv;
1360 g_return_val_if_fail (self, NULL);
1362 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1364 return (const gchar*) priv->msg_uid;
1368 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1371 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1372 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1373 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1377 is_active = gtk_toggle_action_get_active (toggle);
1380 gtk_widget_show (priv->find_toolbar);
1381 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1383 gtk_widget_hide (priv->find_toolbar);
1386 /* update the toggle buttons status */
1387 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1388 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1389 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1390 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1395 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1396 ModestMsgViewWindow *obj)
1398 GtkToggleAction *toggle;
1399 ModestWindowPrivate *parent_priv;
1400 ModestMsgViewWindowPrivate *priv;
1402 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1403 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1405 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1406 gtk_toggle_action_set_active (toggle, FALSE);
1407 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1411 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1412 ModestMsgViewWindow *obj)
1414 gchar *current_search;
1415 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1417 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1418 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1422 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1424 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1425 g_free (current_search);
1426 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1430 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1432 g_free (priv->last_search);
1433 priv->last_search = g_strdup (current_search);
1434 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1437 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1438 g_free (priv->last_search);
1439 priv->last_search = NULL;
1441 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1442 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1445 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1446 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1447 g_free (priv->last_search);
1448 priv->last_search = NULL;
1450 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1451 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1455 g_free (current_search);
1460 modest_msg_view_window_set_zoom (ModestWindow *window,
1463 ModestMsgViewWindowPrivate *priv;
1464 ModestWindowPrivate *parent_priv;
1465 GtkAction *action = NULL;
1466 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1468 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1470 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1471 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1472 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1474 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1475 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1477 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1481 modest_msg_view_window_get_zoom (ModestWindow *window)
1483 ModestMsgViewWindowPrivate *priv;
1485 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1487 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1488 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1492 modest_msg_view_window_zoom_plus (ModestWindow *window)
1494 ModestWindowPrivate *parent_priv;
1495 GtkRadioAction *zoom_radio_action;
1496 GSList *group, *node;
1498 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1499 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1500 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1502 group = gtk_radio_action_get_group (zoom_radio_action);
1504 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1505 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1509 for (node = group; node != NULL; node = g_slist_next (node)) {
1510 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1511 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1519 modest_msg_view_window_zoom_minus (ModestWindow *window)
1521 ModestWindowPrivate *parent_priv;
1522 GtkRadioAction *zoom_radio_action;
1523 GSList *group, *node;
1525 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1526 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1527 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1529 group = gtk_radio_action_get_group (zoom_radio_action);
1531 for (node = group; node != NULL; node = g_slist_next (node)) {
1532 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1533 if (node->next != NULL) {
1534 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1537 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1547 modest_msg_view_window_key_event (GtkWidget *window,
1553 focus = gtk_window_get_focus (GTK_WINDOW (window));
1555 /* for the find toolbar case */
1556 if (focus && GTK_IS_ENTRY (focus)) {
1557 if (event->keyval == GDK_BackSpace) {
1559 copy = gdk_event_copy ((GdkEvent *) event);
1560 gtk_widget_event (focus, copy);
1561 gdk_event_free (copy);
1566 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1567 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1568 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1569 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1570 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1571 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1572 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1573 gboolean return_value;
1575 if (event->type == GDK_KEY_RELEASE) {
1576 GtkScrollType scroll_type;
1578 switch (event->keyval) {
1581 scroll_type = GTK_SCROLL_STEP_UP; break;
1584 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1586 case GDK_KP_Page_Up:
1587 scroll_type = GTK_SCROLL_PAGE_UP; break;
1589 case GDK_KP_Page_Down:
1590 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1593 scroll_type = GTK_SCROLL_START; break;
1596 scroll_type = GTK_SCROLL_END; break;
1597 default: scroll_type = GTK_SCROLL_NONE;
1600 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",
1601 scroll_type, FALSE, &return_value);
1612 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1615 ModestMsgViewWindowPrivate *priv;
1616 GtkTreeIter tmp_iter;
1617 gboolean is_last_selected;
1619 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1620 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1622 /*if no model (so no rows at all), then virtually we are the last*/
1623 if (!priv->header_model || !priv->row_reference)
1626 path = gtk_tree_row_reference_get_path (priv->row_reference);
1630 is_last_selected = TRUE;
1631 while (is_last_selected) {
1633 gtk_tree_path_next (path);
1634 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1636 gtk_tree_model_get (priv->header_model, &tmp_iter,
1637 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1640 if (msg_is_visible (header, priv->is_outbox))
1641 is_last_selected = FALSE;
1642 g_object_unref(G_OBJECT(header));
1645 gtk_tree_path_free (path);
1646 return is_last_selected;
1650 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1652 ModestMsgViewWindowPrivate *priv;
1654 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1655 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1657 return priv->header_model != NULL;
1661 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1663 ModestMsgViewWindowPrivate *priv;
1665 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1666 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1668 return priv->is_search_result;
1672 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1674 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1676 if (!check_outbox) {
1679 ModestTnySendQueueStatus status;
1680 status = modest_tny_all_send_queues_get_msg_status (header);
1681 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1682 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1687 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1690 ModestMsgViewWindowPrivate *priv;
1691 gboolean is_first_selected;
1692 GtkTreeIter tmp_iter;
1693 /* gchar * path_string;*/
1695 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1696 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1698 /*if no model (so no rows at all), then virtually we are the first*/
1699 if (!priv->header_model || !priv->row_reference)
1702 path = gtk_tree_row_reference_get_path (priv->row_reference);
1706 /* path_string = gtk_tree_path_to_string (path);
1707 is_first_selected = strcmp (path_string, "0");
1709 g_free (path_string);
1710 gtk_tree_path_free (path);
1712 return is_first_selected;*/
1714 is_first_selected = TRUE;
1715 while (is_first_selected) {
1717 if(!gtk_tree_path_prev (path))
1719 /* Here the 'if' is needless for logic, but let make sure
1720 * iter is valid for gtk_tree_model_get. */
1721 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1723 gtk_tree_model_get (priv->header_model, &tmp_iter,
1724 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1727 if (msg_is_visible (header, priv->is_outbox))
1728 is_first_selected = FALSE;
1729 g_object_unref(G_OBJECT(header));
1732 gtk_tree_path_free (path);
1733 return is_first_selected;
1738 GtkTreeRowReference *row_reference;
1742 message_reader_performer (gboolean canceled,
1744 GtkWindow *parent_window,
1745 TnyAccount *account,
1748 ModestMailOperation *mail_op = NULL;
1749 MsgReaderInfo *info;
1751 info = (MsgReaderInfo *) user_data;
1752 if (canceled || err) {
1756 /* Register the header - it'll be unregistered in the callback */
1757 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1759 /* New mail operation */
1760 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1761 modest_ui_actions_disk_operations_error_handler,
1764 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1765 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1766 g_object_unref (mail_op);
1768 /* Update dimming rules */
1769 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1770 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1773 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1774 g_object_unref (info->header);
1775 g_slice_free (MsgReaderInfo, info);
1780 * Reads the message whose summary item is @header. It takes care of
1781 * several things, among others:
1783 * If the message was not previously downloaded then ask the user
1784 * before downloading. If there is no connection launch the connection
1785 * dialog. Update toolbar dimming rules.
1787 * Returns: TRUE if the mail operation was started, otherwise if the
1788 * user do not want to download the message, or if the user do not
1789 * want to connect, then the operation is not issued
1792 message_reader (ModestMsgViewWindow *window,
1793 ModestMsgViewWindowPrivate *priv,
1795 GtkTreeRowReference *row_reference)
1797 gboolean already_showing = FALSE;
1798 ModestWindow *msg_window = NULL;
1799 ModestWindowMgr *mgr;
1800 TnyAccount *account;
1802 MsgReaderInfo *info;
1804 g_return_val_if_fail (row_reference != NULL, FALSE);
1806 mgr = modest_runtime_get_window_mgr ();
1807 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1808 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1811 gtk_window_present (GTK_WINDOW (msg_window));
1812 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1816 /* Msg download completed */
1817 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1818 /* Ask the user if he wants to download the message if
1820 if (!tny_device_is_online (modest_runtime_get_device())) {
1821 GtkResponseType response;
1823 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1824 _("mcen_nc_get_msg"));
1825 if (response == GTK_RESPONSE_CANCEL)
1828 folder = tny_header_get_folder (header);
1829 info = g_slice_new (MsgReaderInfo);
1830 info->header = g_object_ref (header);
1831 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1833 /* Offer the connection dialog if necessary */
1834 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1836 TNY_FOLDER_STORE (folder),
1837 message_reader_performer,
1839 g_object_unref (folder);
1844 folder = tny_header_get_folder (header);
1845 account = tny_folder_get_account (folder);
1846 info = g_slice_new (MsgReaderInfo);
1847 info->header = g_object_ref (header);
1848 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1850 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1851 g_object_unref (account);
1852 g_object_unref (folder);
1858 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1860 ModestMsgViewWindowPrivate *priv;
1861 GtkTreePath *path= NULL;
1862 GtkTreeIter tmp_iter;
1864 gboolean retval = TRUE;
1865 GtkTreeRowReference *row_reference = NULL;
1867 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1868 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1870 if (!priv->row_reference)
1873 /* Update the next row reference if it's not valid. This could
1874 happen if for example the header which it was pointing to,
1875 was deleted. The best place to do it is in the row-deleted
1876 handler but the tinymail model do not work like the glib
1877 tree models and reports the deletion when the row is still
1879 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1880 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1881 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1882 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1885 if (priv->next_row_reference)
1886 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1890 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1892 gtk_tree_model_get_iter (priv->header_model,
1895 gtk_tree_path_free (path);
1897 gtk_tree_model_get (priv->header_model, &tmp_iter,
1898 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1901 /* Read the message & show it */
1902 if (!message_reader (window, priv, header, row_reference)) {
1905 gtk_tree_row_reference_free (row_reference);
1908 g_object_unref (header);
1914 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1916 ModestMsgViewWindowPrivate *priv = NULL;
1918 gboolean finished = FALSE;
1919 gboolean retval = FALSE;
1921 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1922 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1924 /* Return inmediatly if there is no header model */
1925 if (!priv->header_model || !priv->row_reference)
1928 path = gtk_tree_row_reference_get_path (priv->row_reference);
1929 while (!finished && gtk_tree_path_prev (path)) {
1933 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1934 gtk_tree_model_get (priv->header_model, &iter,
1935 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1939 if (msg_is_visible (header, priv->is_outbox)) {
1940 GtkTreeRowReference *row_reference;
1941 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1942 /* Read the message & show it */
1943 retval = message_reader (window, priv, header, row_reference);
1944 gtk_tree_row_reference_free (row_reference);
1948 g_object_unref (header);
1952 gtk_tree_path_free (path);
1957 view_msg_cb (ModestMailOperation *mail_op,
1964 ModestMsgViewWindow *self = NULL;
1965 ModestMsgViewWindowPrivate *priv = NULL;
1966 GtkTreeRowReference *row_reference = NULL;
1968 /* Unregister the header (it was registered before creating the mail operation) */
1969 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1971 row_reference = (GtkTreeRowReference *) user_data;
1973 gtk_tree_row_reference_free (row_reference);
1977 /* If there was any error */
1978 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1979 gtk_tree_row_reference_free (row_reference);
1983 /* Get the window */
1984 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1985 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1986 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1988 /* Update the row reference */
1989 if (priv->row_reference != NULL) {
1990 gtk_tree_row_reference_free (priv->row_reference);
1991 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1992 if (priv->next_row_reference != NULL) {
1993 gtk_tree_row_reference_free (priv->next_row_reference);
1995 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1996 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1999 /* Mark header as read */
2000 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2001 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2003 /* Set new message */
2004 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2005 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2006 modest_msg_view_window_update_priority (self);
2007 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2008 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2011 /* Set the new message uid of the window */
2012 if (priv->msg_uid) {
2013 g_free (priv->msg_uid);
2014 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2017 /* Notify the observers */
2018 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2019 0, priv->header_model, priv->row_reference);
2022 g_object_unref (self);
2023 gtk_tree_row_reference_free (row_reference);
2027 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2029 ModestMsgViewWindowPrivate *priv;
2031 TnyFolderType folder_type;
2033 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2035 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2037 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2041 folder = tny_msg_get_folder (msg);
2043 folder_type = modest_tny_folder_guess_folder_type (folder);
2044 g_object_unref (folder);
2046 g_object_unref (msg);
2054 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2056 ModestMsgViewWindowPrivate *priv;
2057 TnyHeader *header = NULL;
2058 TnyHeaderFlags flags = 0;
2060 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2062 if (priv->header_model && priv->row_reference) {
2064 GtkTreePath *path = NULL;
2066 path = gtk_tree_row_reference_get_path (priv->row_reference);
2067 g_return_if_fail (path != NULL);
2068 gtk_tree_model_get_iter (priv->header_model,
2070 gtk_tree_row_reference_get_path (priv->row_reference));
2072 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2074 gtk_tree_path_free (path);
2077 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2079 header = tny_msg_get_header (msg);
2080 g_object_unref (msg);
2085 flags = tny_header_get_flags (header);
2086 g_object_unref(G_OBJECT(header));
2089 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2094 toolbar_resize (ModestMsgViewWindow *self)
2096 ModestMsgViewWindowPrivate *priv = NULL;
2097 ModestWindowPrivate *parent_priv = NULL;
2099 gint static_button_size;
2100 ModestWindowMgr *mgr;
2102 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2103 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2104 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2106 mgr = modest_runtime_get_window_mgr ();
2107 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2109 if (parent_priv->toolbar) {
2110 /* left size buttons */
2111 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2112 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2113 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2114 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2115 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2116 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2117 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2118 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2119 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2120 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2121 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2122 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2123 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2124 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2125 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2126 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2128 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2129 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2130 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2131 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2132 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2133 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2134 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2135 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2141 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2143 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2144 ModestWindowPrivate *parent_priv;
2145 ModestWindowMgr *mgr;
2146 gboolean is_fullscreen;
2147 GtkAction *fs_toggle_action;
2150 mgr = modest_runtime_get_window_mgr ();
2151 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2153 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2155 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2156 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2157 if (is_fullscreen != active) {
2158 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2160 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2168 modest_msg_view_window_show_toolbar (ModestWindow *self,
2169 gboolean show_toolbar)
2171 ModestMsgViewWindowPrivate *priv = NULL;
2172 ModestWindowPrivate *parent_priv;
2173 GtkWidget *reply_button = NULL, *menu = NULL;
2174 GtkWidget *placeholder = NULL;
2176 const gchar *action_name;
2179 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2180 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2182 /* Set optimized view status */
2183 priv->optimized_view = !show_toolbar;
2185 if (!parent_priv->toolbar) {
2186 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2188 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2190 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2191 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2192 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2193 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2194 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2196 /* Add ProgressBar (Transfer toolbar) */
2197 priv->progress_bar = modest_progress_bar_new ();
2198 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2199 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2200 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2201 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2202 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2204 /* Connect cancel 'clicked' signal to abort progress mode */
2205 g_signal_connect(priv->cancel_toolitem, "clicked",
2206 G_CALLBACK(cancel_progressbar),
2209 /* Add it to the observers list */
2210 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2213 hildon_window_add_toolbar (HILDON_WINDOW (self),
2214 GTK_TOOLBAR (parent_priv->toolbar));
2216 /* Set reply button tap and hold menu */
2217 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2218 "/ToolBar/ToolbarMessageReply");
2219 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2220 "/ToolbarReplyCSM");
2221 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2225 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2226 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2227 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2229 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2230 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2231 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2233 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2236 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2237 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2240 /* Update also the actions (to update the toggles in the
2241 menus), we have to do it manually because some other window
2242 of the same time could have changed it (remember that the
2243 toolbar fullscreen mode is shared by all the windows of the
2245 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2246 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2248 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2250 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2251 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2256 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2258 ModestMsgViewWindow *window)
2260 if (!GTK_WIDGET_VISIBLE (window))
2263 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2267 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2269 ModestMsgViewWindowPrivate *priv;
2271 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2272 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2274 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2278 cancel_progressbar (GtkToolButton *toolbutton,
2279 ModestMsgViewWindow *self)
2282 ModestMsgViewWindowPrivate *priv;
2284 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2286 /* Get operation observers and cancel its current operation */
2287 tmp = priv->progress_widgets;
2289 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2290 tmp=g_slist_next(tmp);
2294 observers_empty (ModestMsgViewWindow *self)
2297 ModestMsgViewWindowPrivate *priv;
2298 gboolean is_empty = TRUE;
2299 guint pending_ops = 0;
2301 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2302 tmp = priv->progress_widgets;
2304 /* Check all observers */
2305 while (tmp && is_empty) {
2306 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2307 is_empty = pending_ops == 0;
2309 tmp = g_slist_next(tmp);
2316 on_account_removed (TnyAccountStore *account_store,
2317 TnyAccount *account,
2320 /* Do nothing if it's a transport account, because we only
2321 show the messages of a store account */
2322 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2323 const gchar *parent_acc = NULL;
2324 const gchar *our_acc = NULL;
2326 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2327 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2329 /* Close this window if I'm showing a message of the removed account */
2330 if (strcmp (parent_acc, our_acc) == 0)
2331 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2336 on_mail_operation_started (ModestMailOperation *mail_op,
2339 ModestMsgViewWindow *self;
2340 ModestMailOperationTypeOperation op_type;
2342 ModestMsgViewWindowPrivate *priv;
2343 GObject *source = NULL;
2345 self = MODEST_MSG_VIEW_WINDOW (user_data);
2346 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2347 op_type = modest_mail_operation_get_type_operation (mail_op);
2348 tmp = priv->progress_widgets;
2349 source = modest_mail_operation_get_source(mail_op);
2350 if (G_OBJECT (self) == source) {
2351 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2352 set_toolbar_transfer_mode(self);
2354 modest_progress_object_add_operation (
2355 MODEST_PROGRESS_OBJECT (tmp->data),
2357 tmp = g_slist_next (tmp);
2361 g_object_unref (source);
2365 on_mail_operation_finished (ModestMailOperation *mail_op,
2368 ModestMsgViewWindow *self;
2369 ModestMailOperationTypeOperation op_type;
2371 ModestMsgViewWindowPrivate *priv;
2373 self = MODEST_MSG_VIEW_WINDOW (user_data);
2374 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2375 op_type = modest_mail_operation_get_type_operation (mail_op);
2376 tmp = priv->progress_widgets;
2378 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2380 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2382 tmp = g_slist_next (tmp);
2385 /* If no more operations are being observed, NORMAL mode is enabled again */
2386 if (observers_empty (self)) {
2387 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2390 /* Update dimming rules. We have to do this right here
2391 and not in view_msg_cb because at that point the
2392 transfer mode is still enabled so the dimming rule
2393 won't let the user delete the message that has been
2394 readed for example */
2395 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2396 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2401 on_queue_changed (ModestMailOperationQueue *queue,
2402 ModestMailOperation *mail_op,
2403 ModestMailOperationQueueNotification type,
2404 ModestMsgViewWindow *self)
2406 ModestMsgViewWindowPrivate *priv;
2408 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2410 /* If this operations was created by another window, do nothing */
2411 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2414 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2415 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2417 "operation-started",
2418 G_CALLBACK (on_mail_operation_started),
2420 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2422 "operation-finished",
2423 G_CALLBACK (on_mail_operation_finished),
2425 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2426 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2428 "operation-started");
2429 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2431 "operation-finished");
2436 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2438 ModestMsgViewWindowPrivate *priv;
2439 TnyList *selected_attachments = NULL;
2441 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2442 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2444 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2446 return selected_attachments;
2450 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2452 ModestMsgViewWindowPrivate *priv;
2453 const gchar *msg_uid;
2454 gchar *attachment_uid = NULL;
2455 gint attachment_index = 0;
2456 TnyList *attachments;
2458 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2459 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2460 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2462 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2463 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2464 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2465 g_object_unref (attachments);
2467 if (msg_uid && attachment_index >= 0) {
2468 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2471 if (mime_part == NULL) {
2472 gboolean error = FALSE;
2473 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2474 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2476 } else if (tny_list_get_length (selected_attachments) > 1) {
2477 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2481 iter = tny_list_create_iterator (selected_attachments);
2482 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2483 g_object_unref (iter);
2485 g_object_unref (selected_attachments);
2490 g_object_ref (mime_part);
2493 if (tny_mime_part_is_purged (mime_part)) {
2494 g_object_unref (mime_part);
2498 if (!modest_tny_mime_part_is_msg (mime_part)) {
2499 gchar *filepath = NULL;
2500 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2501 const gchar *content_type;
2502 gboolean show_error_banner = FALSE;
2504 TnyFsStream *temp_stream = NULL;
2505 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2508 if (temp_stream != NULL) {
2509 content_type = tny_mime_part_get_content_type (mime_part);
2510 if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2511 /* make the file read-only */
2512 if (g_chmod(filepath, 0444) != 0)
2513 g_warning ("%s: failed to set file '%s' to read-only: %s",
2514 __FUNCTION__, filepath, strerror(errno));
2516 modest_platform_activate_file (filepath, content_type);
2518 /* error while saving attachment, maybe cerm_device_memory_full */
2519 show_error_banner = TRUE;
2521 g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2525 g_object_unref (temp_stream);
2527 /* NOTE: files in the temporary area will be automatically
2528 * cleaned after some time if they are no longer in use */
2530 if (filepath != NULL) {
2531 /* the file may already exist but it isn't writable,
2532 * let's try to open it anyway */
2533 content_type = tny_mime_part_get_content_type (mime_part);
2534 modest_platform_activate_file (filepath, content_type);
2537 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2538 show_error_banner = TRUE;
2541 if (show_error_banner)
2542 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2544 /* message attachment */
2545 TnyHeader *header = NULL;
2546 ModestWindowMgr *mgr;
2547 ModestWindow *msg_win = NULL;
2550 header = tny_msg_get_header (TNY_MSG (mime_part));
2551 mgr = modest_runtime_get_window_mgr ();
2552 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2555 if (msg_win) /* there is already a window for this uid; top it */
2556 gtk_window_present (GTK_WINDOW(msg_win));
2558 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2559 * thus, we don't do anything */
2560 g_warning ("window for is already being created");
2562 /* it's not found, so create a new window for it */
2563 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2564 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2566 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2567 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2568 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2569 modest_window_get_zoom (MODEST_WINDOW (window)));
2570 modest_window_mgr_register_window (mgr, msg_win);
2571 gtk_widget_show_all (GTK_WIDGET (msg_win));
2574 g_object_unref (mime_part);
2587 GnomeVFSResult result;
2590 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2591 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2592 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2593 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2596 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2600 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2601 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2602 g_free (pair->filename);
2603 g_object_unref (pair->part);
2604 g_slice_free (SaveMimePartPair, pair);
2606 g_list_free (info->pairs);
2609 gtk_widget_destroy (info->banner);
2610 g_slice_free (SaveMimePartInfo, info);
2615 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2617 if (info->pairs != NULL) {
2618 save_mime_part_to_file (info);
2620 /* This is a GDK lock because we are an idle callback and
2621 * hildon_banner_show_information is or does Gtk+ code */
2623 gdk_threads_enter (); /* CHECKED */
2624 save_mime_part_info_free (info, TRUE);
2625 if (info->result == GNOME_VFS_OK) {
2626 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2627 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2628 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2629 "cerm_device_memory_full"));
2631 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2633 gdk_threads_leave (); /* CHECKED */
2640 save_mime_part_to_file (SaveMimePartInfo *info)
2642 GnomeVFSHandle *handle;
2644 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2646 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2647 if (info->result == GNOME_VFS_OK) {
2648 stream = tny_vfs_stream_new (handle);
2649 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2650 info->result = GNOME_VFS_ERROR_IO;
2652 g_object_unref (G_OBJECT (stream));
2653 g_object_unref (pair->part);
2654 g_slice_free (SaveMimePartPair, pair);
2655 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2657 save_mime_part_info_free (info, FALSE);
2660 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2665 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2667 gboolean is_ok = TRUE;
2668 gint replaced_files = 0;
2669 const GList *files = info->pairs;
2672 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2673 SaveMimePartPair *pair = iter->data;
2674 if (modest_utils_file_exists (pair->filename)) {
2678 if (replaced_files) {
2679 GtkWidget *confirm_overwrite_dialog;
2680 const gchar *message = (replaced_files == 1) ?
2681 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2682 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2683 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2686 gtk_widget_destroy (confirm_overwrite_dialog);
2690 save_mime_part_info_free (info, TRUE);
2692 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2693 _CS("sfil_ib_saving"));
2694 info->banner = banner;
2695 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2702 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2704 ModestMsgViewWindowPrivate *priv;
2705 GList *files_to_save = NULL;
2706 GtkWidget *save_dialog = NULL;
2707 gchar *folder = NULL;
2708 const gchar *filename = NULL;
2709 gchar *save_multiple_str = NULL;
2711 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2712 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2714 if (mime_parts == NULL) {
2715 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2716 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2719 g_object_ref (mime_parts);
2722 /* prepare dialog */
2723 if (tny_list_get_length (mime_parts) == 1) {
2725 /* only one attachment selected */
2726 iter = tny_list_create_iterator (mime_parts);
2727 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2728 g_object_unref (iter);
2729 if (!modest_tny_mime_part_is_msg (mime_part) &&
2730 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2731 !tny_mime_part_is_purged (mime_part)) {
2732 filename = tny_mime_part_get_filename (mime_part);
2734 /* TODO: show any error? */
2735 g_warning ("Tried to save a non-file attachment");
2736 g_object_unref (mime_parts);
2739 g_object_unref (mime_part);
2741 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2742 tny_list_get_length (mime_parts));
2745 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2746 GTK_FILE_CHOOSER_ACTION_SAVE);
2749 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2750 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2754 if (filename != NULL)
2755 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2758 /* if multiple, set multiple string */
2759 if (save_multiple_str) {
2760 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2761 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2765 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2766 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2768 if (!modest_utils_folder_writable (chooser_uri)) {
2769 hildon_banner_show_information
2770 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2774 iter = tny_list_create_iterator (mime_parts);
2775 while (!tny_iterator_is_done (iter)) {
2776 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2778 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2779 !tny_mime_part_is_purged (mime_part) &&
2780 (tny_mime_part_get_filename (mime_part) != NULL)) {
2781 SaveMimePartPair *pair;
2783 pair = g_slice_new0 (SaveMimePartPair);
2784 if (save_multiple_str) {
2785 gchar *escaped = gnome_vfs_escape_slashes (
2786 tny_mime_part_get_filename (mime_part));
2787 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2790 pair->filename = g_strdup (chooser_uri);
2792 pair->part = mime_part;
2793 files_to_save = g_list_prepend (files_to_save, pair);
2795 tny_iterator_next (iter);
2797 g_object_unref (iter);
2799 g_free (chooser_uri);
2802 gtk_widget_destroy (save_dialog);
2804 g_object_unref (mime_parts);
2806 if (files_to_save != NULL) {
2807 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2808 info->pairs = files_to_save;
2809 info->result = TRUE;
2810 save_mime_parts_to_file_with_checks (info);
2815 show_remove_attachment_information (gpointer userdata)
2817 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2818 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2820 /* We're outside the main lock */
2821 gdk_threads_enter ();
2823 if (priv->remove_attachment_banner != NULL) {
2824 gtk_widget_destroy (priv->remove_attachment_banner);
2825 g_object_unref (priv->remove_attachment_banner);
2828 priv->remove_attachment_banner = g_object_ref (
2829 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2831 gdk_threads_leave ();
2837 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2839 ModestMsgViewWindowPrivate *priv;
2840 TnyList *mime_parts = NULL;
2841 gchar *confirmation_message;
2846 /* TnyFolder *folder; */
2848 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2849 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2852 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2854 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2856 /* Remove already purged messages from mime parts list */
2857 iter = tny_list_create_iterator (mime_parts);
2858 while (!tny_iterator_is_done (iter)) {
2859 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2860 tny_iterator_next (iter);
2861 if (tny_mime_part_is_purged (part)) {
2862 tny_list_remove (mime_parts, (GObject *) part);
2864 g_object_unref (part);
2866 g_object_unref (iter);
2868 if (tny_list_get_length (mime_parts) == 0) {
2869 g_object_unref (mime_parts);
2873 n_attachments = tny_list_get_length (mime_parts);
2874 if (n_attachments == 1) {
2878 iter = tny_list_create_iterator (mime_parts);
2879 part = (TnyMimePart *) tny_iterator_get_current (iter);
2880 g_object_unref (iter);
2881 if (modest_tny_mime_part_is_msg (part)) {
2883 header = tny_msg_get_header (TNY_MSG (part));
2884 filename = tny_header_dup_subject (header);
2885 g_object_unref (header);
2886 if (filename == NULL)
2887 filename = g_strdup (_("mail_va_no_subject"));
2889 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2891 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2893 g_object_unref (part);
2895 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2896 "mcen_nc_purge_files_text",
2897 n_attachments), n_attachments);
2899 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2900 confirmation_message);
2901 g_free (confirmation_message);
2903 if (response != GTK_RESPONSE_OK) {
2904 g_object_unref (mime_parts);
2908 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2909 /* folder = tny_msg_get_folder (msg); */
2910 /* tny_msg_uncache_attachments (msg); */
2911 /* tny_folder_refresh (folder, NULL); */
2912 /* g_object_unref (folder); */
2914 iter = tny_list_create_iterator (mime_parts);
2915 while (!tny_iterator_is_done (iter)) {
2918 part = (TnyMimePart *) tny_iterator_get_current (iter);
2919 tny_mime_part_set_purged (TNY_MIME_PART (part));
2920 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2921 g_object_unref (part);
2922 tny_iterator_next (iter);
2924 g_object_unref (iter);
2926 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2927 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2928 tny_msg_rewrite_cache (msg);
2929 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2930 g_object_unref (msg);
2932 g_object_unref (mime_parts);
2934 if (priv->purge_timeout > 0) {
2935 g_source_remove (priv->purge_timeout);
2936 priv->purge_timeout = 0;
2939 if (priv->remove_attachment_banner) {
2940 gtk_widget_destroy (priv->remove_attachment_banner);
2941 g_object_unref (priv->remove_attachment_banner);
2942 priv->remove_attachment_banner = NULL;
2950 update_window_title (ModestMsgViewWindow *window)
2952 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2954 TnyHeader *header = NULL;
2955 gchar *subject = NULL;
2957 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2960 header = tny_msg_get_header (msg);
2961 subject = tny_header_dup_subject (header);
2962 g_object_unref (header);
2963 g_object_unref (msg);
2966 if ((subject == NULL)||(subject[0] == '\0')) {
2968 subject = g_strdup (_("mail_va_no_subject"));
2971 gtk_window_set_title (GTK_WINDOW (window), subject);
2975 static void on_move_focus (GtkWidget *widget,
2976 GtkDirectionType direction,
2979 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");