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,
689 GtkTreeIter tmp_iter;
691 GtkTreePath *next = NULL;
692 gboolean retval = FALSE;
694 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
696 path = gtk_tree_row_reference_get_path (*row_reference);
697 gtk_tree_model_get_iter (model, &tmp_iter, path);
698 gtk_tree_row_reference_free (*row_reference);
699 *row_reference = NULL;
701 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
702 next = gtk_tree_model_get_path (model, &tmp_iter);
703 *row_reference = gtk_tree_row_reference_new (model, next);
705 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
706 next = gtk_tree_model_get_path (model, &tmp_iter);
708 /* Ensure that we are not selecting the same */
709 if (gtk_tree_path_compare (path, next) != 0) {
710 *row_reference = gtk_tree_row_reference_new (model, next);
716 gtk_tree_path_free (path);
718 gtk_tree_path_free (next);
723 /* TODO: This should be in _init(), with the parameters as properties. */
725 modest_msg_view_window_construct (ModestMsgViewWindow *self,
726 const gchar *modest_account_name,
727 const gchar *msg_uid)
730 ModestMsgViewWindowPrivate *priv = NULL;
731 ModestWindowPrivate *parent_priv = NULL;
732 ModestDimmingRulesGroup *menu_rules_group = NULL;
733 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
734 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
736 obj = G_OBJECT (self);
737 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
738 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
740 priv->msg_uid = g_strdup (msg_uid);
743 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
744 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
745 gtk_widget_show (parent_priv->menubar);
746 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
748 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
749 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
750 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
752 /* Add common dimming rules */
753 modest_dimming_rules_group_add_rules (menu_rules_group,
754 modest_msg_view_menu_dimming_entries,
755 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
756 MODEST_WINDOW (self));
757 modest_dimming_rules_group_add_rules (toolbar_rules_group,
758 modest_msg_view_toolbar_dimming_entries,
759 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
760 MODEST_WINDOW (self));
761 modest_dimming_rules_group_add_rules (clipboard_rules_group,
762 modest_msg_view_clipboard_dimming_entries,
763 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
764 MODEST_WINDOW (self));
766 /* Insert dimming rules group for this window */
767 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
768 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
769 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
770 g_object_unref (menu_rules_group);
771 g_object_unref (toolbar_rules_group);
772 g_object_unref (clipboard_rules_group);
774 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
776 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
778 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);
779 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
780 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
781 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
782 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
783 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
784 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
785 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
786 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
787 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
788 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
790 g_signal_connect (G_OBJECT (obj), "key-release-event",
791 G_CALLBACK (modest_msg_view_window_key_event),
794 g_signal_connect (G_OBJECT (obj), "key-press-event",
795 G_CALLBACK (modest_msg_view_window_key_event),
798 g_signal_connect (G_OBJECT (obj), "window-state-event",
799 G_CALLBACK (modest_msg_view_window_window_state_event),
802 g_signal_connect (G_OBJECT (obj), "move-focus",
803 G_CALLBACK (on_move_focus), obj);
805 /* Mail Operation Queue */
806 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
808 G_CALLBACK (on_queue_changed),
811 /* Account manager */
812 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
814 G_CALLBACK(on_account_removed),
817 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
819 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
820 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
821 priv->last_search = NULL;
823 /* Init the clipboard actions dim status */
824 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
826 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
831 /* FIXME: parameter checks */
833 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
834 const gchar *modest_account_name,
835 const gchar *msg_uid,
837 GtkTreeRowReference *row_reference)
839 ModestMsgViewWindow *window = NULL;
840 ModestMsgViewWindowPrivate *priv = NULL;
841 TnyFolder *header_folder = NULL;
842 ModestHeaderView *header_view = NULL;
843 ModestWindow *main_window = NULL;
844 ModestWindowMgr *mgr = NULL;
847 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
850 mgr = modest_runtime_get_window_mgr ();
851 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
852 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
854 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
856 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
858 /* Remember the message list's TreeModel so we can detect changes
859 * and change the list selection when necessary: */
861 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
863 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
864 MODEST_MAIN_WINDOW(main_window),
865 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
868 if (header_view != NULL){
869 header_folder = modest_header_view_get_folder(header_view);
870 /* This could happen if the header folder was
871 unseleted before opening this msg window (for
872 example if the user selects an account in the
873 folder view of the main window */
875 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
876 priv->header_folder_id = tny_folder_get_id(header_folder);
877 g_assert(priv->header_folder_id != NULL);
878 g_object_unref(header_folder);
882 /* Setup row references and connect signals */
883 priv->header_model = g_object_ref (model);
886 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
887 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
888 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
890 priv->row_reference = NULL;
891 priv->next_row_reference = NULL;
894 /* Connect signals */
895 priv->row_changed_handler =
896 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
897 G_CALLBACK(modest_msg_view_window_on_row_changed),
899 priv->row_deleted_handler =
900 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
901 G_CALLBACK(modest_msg_view_window_on_row_deleted),
903 priv->row_inserted_handler =
904 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
905 G_CALLBACK(modest_msg_view_window_on_row_inserted),
907 priv->rows_reordered_handler =
908 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
909 G_CALLBACK(modest_msg_view_window_on_row_reordered),
912 if (header_view != NULL){
913 modest_header_view_add_observer(header_view,
914 MODEST_HEADER_VIEW_OBSERVER(window));
917 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
918 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
919 gtk_widget_show_all (GTK_WIDGET (window));
920 modest_msg_view_window_update_priority (window);
922 /* Check dimming rules */
923 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
924 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
925 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
927 return MODEST_WINDOW(window);
931 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
932 const gchar *modest_account_name,
933 const gchar *msg_uid)
935 ModestMsgViewWindow *window = NULL;
936 ModestMsgViewWindowPrivate *priv = NULL;
937 ModestWindowMgr *mgr = NULL;
939 mgr = modest_runtime_get_window_mgr ();
940 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
941 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
942 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
944 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
946 /* Remember that this is a search result,
947 * so we can disable some UI appropriately: */
948 priv->is_search_result = TRUE;
950 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
952 update_window_title (window);
953 gtk_widget_show_all (GTK_WIDGET (window));
954 modest_msg_view_window_update_priority (window);
956 /* Check dimming rules */
957 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
958 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
959 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
961 return MODEST_WINDOW(window);
965 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
966 const gchar *modest_account_name,
967 const gchar *msg_uid)
970 ModestMsgViewWindowPrivate *priv;
971 ModestWindowMgr *mgr = NULL;
973 g_return_val_if_fail (msg, NULL);
974 mgr = modest_runtime_get_window_mgr ();
975 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
976 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
977 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
978 modest_account_name, msg_uid);
980 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
981 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
983 gtk_widget_show_all (GTK_WIDGET (obj));
985 /* Check dimming rules */
986 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
987 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
988 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
990 return MODEST_WINDOW(obj);
994 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
997 ModestMsgViewWindow *window)
999 check_dimming_rules_after_change (window);
1003 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1005 ModestMsgViewWindow *window)
1007 check_dimming_rules_after_change (window);
1011 check_dimming_rules_after_change_in_idle (gpointer data)
1013 /* The window could have dissapeared */
1014 if (MODEST_IS_WINDOW (data)) {
1015 ModestWindow *win = MODEST_WINDOW (data);
1016 gdk_threads_enter ();
1017 modest_ui_actions_check_menu_dimming_rules (win);
1018 modest_ui_actions_check_toolbar_dimming_rules (win);
1019 gdk_threads_leave ();
1026 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1028 static guint dimming_delayer = 0;
1030 if (dimming_delayer > 0)
1031 g_source_remove (dimming_delayer);
1033 /* We're expecting a lot of changes at the same time so don't
1034 need to check dimming rules for every change that
1036 dimming_delayer = g_timeout_add (100, check_dimming_rules_after_change_in_idle, window);
1040 /* On insertions we check if the folder still has the message we are
1041 * showing or do not. If do not, we do nothing. Which means we are still
1042 * not attached to any header folder and thus next/prev buttons are
1043 * still dimmed. Once the message that is shown by msg-view is found, the
1044 * new model of header-view will be attached and the references will be set.
1045 * On each further insertions dimming rules will be checked. However
1046 * this requires extra CPU time at least works.
1047 * (An message might be deleted from TnyFolder and thus will not be
1048 * inserted into the model again for example if it is removed by the
1049 * imap server and the header view is refreshed.)
1052 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1053 GtkTreePath *tree_path,
1054 GtkTreeIter *tree_iter,
1055 ModestMsgViewWindow *window)
1057 ModestMsgViewWindowPrivate *priv = NULL;
1058 TnyHeader *header = NULL;
1060 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1061 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1063 g_assert (model == priv->header_model);
1065 /* Check if the newly inserted message is the same we are actually
1066 * showing. IF not, we should remain detached from the header model
1067 * and thus prev and next toolbar buttons should remain dimmed. */
1068 gtk_tree_model_get (model, tree_iter,
1069 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1072 if (TNY_IS_HEADER (header)) {
1075 uid = modest_tny_folder_get_header_unique_id (header);
1076 if (!g_str_equal(priv->msg_uid, uid)) {
1077 check_dimming_rules_after_change (window);
1079 g_object_unref (G_OBJECT(header));
1083 g_object_unref(G_OBJECT(header));
1086 if (priv->row_reference) {
1087 gtk_tree_row_reference_free (priv->row_reference);
1090 /* Setup row_reference for the actual msg. */
1091 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1092 if (priv->row_reference == NULL) {
1093 g_warning("No reference for msg header item.");
1097 /* Now set up next_row_reference. */
1098 if (priv->next_row_reference) {
1099 gtk_tree_row_reference_free (priv->next_row_reference);
1102 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1103 select_next_valid_row (priv->header_model,
1104 &(priv->next_row_reference), FALSE);
1106 /* Connect the remaining callbacks to become able to detect
1107 * changes in header-view. */
1108 priv->row_changed_handler =
1109 g_signal_connect (priv->header_model, "row-changed",
1110 G_CALLBACK (modest_msg_view_window_on_row_changed),
1112 priv->row_deleted_handler =
1113 g_signal_connect (priv->header_model, "row-deleted",
1114 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1116 priv->rows_reordered_handler =
1117 g_signal_connect (priv->header_model, "rows-reordered",
1118 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1121 check_dimming_rules_after_change (window);
1125 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1129 ModestMsgViewWindow *window)
1131 ModestMsgViewWindowPrivate *priv = NULL;
1132 gboolean already_changed = FALSE;
1134 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1136 /* If the current row was reordered select the proper next
1137 valid row. The same if the next row reference changes */
1138 if (priv->row_reference &&
1139 gtk_tree_row_reference_valid (priv->row_reference)) {
1141 path = gtk_tree_row_reference_get_path (priv->row_reference);
1142 if (gtk_tree_path_compare (path, arg1) == 0) {
1143 if (priv->next_row_reference) {
1144 gtk_tree_row_reference_free (priv->next_row_reference);
1146 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1147 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE);
1148 already_changed = TRUE;
1150 gtk_tree_path_free (path);
1152 if (!already_changed &&
1153 priv->next_row_reference &&
1154 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1156 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1157 if (gtk_tree_path_compare (path, arg1) == 0) {
1158 if (priv->next_row_reference) {
1159 gtk_tree_row_reference_free (priv->next_row_reference);
1161 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1162 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE);
1164 gtk_tree_path_free (path);
1166 check_dimming_rules_after_change (window);
1169 /* The modest_msg_view_window_update_model_replaced implements update
1170 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1171 * actually belongs to the header-view is the same as the TnyFolder of
1172 * the message of msg-view or not. If they are different, there is
1173 * nothing to do. If they are the same, then the model has replaced and
1174 * the reference in msg-view shall be replaced from the old model to
1175 * the new model. In this case the view will be detached from it's
1176 * header folder. From this point the next/prev buttons are dimmed.
1179 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1180 GtkTreeModel *model,
1181 const gchar *tny_folder_id)
1183 ModestMsgViewWindowPrivate *priv = NULL;
1184 ModestMsgViewWindow *window = NULL;
1186 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1187 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1189 window = MODEST_MSG_VIEW_WINDOW(observer);
1190 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1192 /* If there is an other folder in the header-view then we do
1193 * not care about it's model (msg list). Else if the
1194 * header-view shows the folder the msg shown by us is in, we
1195 * shall replace our model reference and make some check. */
1196 if(model == NULL || tny_folder_id == NULL ||
1197 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1200 /* Model is changed(replaced), so we should forget the old
1201 * one. Because there might be other references and there
1202 * might be some change on the model even if we unreferenced
1203 * it, we need to disconnect our signals here. */
1204 if (priv->header_model) {
1205 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1206 priv->row_changed_handler))
1207 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1208 priv->row_changed_handler);
1209 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1210 priv->row_deleted_handler))
1211 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1212 priv->row_deleted_handler);
1213 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1214 priv->row_inserted_handler))
1215 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1216 priv->row_inserted_handler);
1217 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1218 priv->rows_reordered_handler))
1219 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1220 priv->rows_reordered_handler);
1223 if (priv->row_reference)
1224 gtk_tree_row_reference_free (priv->row_reference);
1225 if (priv->next_row_reference)
1226 gtk_tree_row_reference_free (priv->next_row_reference);
1227 g_object_unref(priv->header_model);
1230 priv->row_changed_handler = 0;
1231 priv->row_deleted_handler = 0;
1232 priv->row_inserted_handler = 0;
1233 priv->rows_reordered_handler = 0;
1234 priv->next_row_reference = NULL;
1235 priv->row_reference = NULL;
1236 priv->header_model = NULL;
1239 priv->header_model = g_object_ref (model);
1241 /* Also we must connect to the new model for row insertions.
1242 * Only for insertions now. We will need other ones only after
1243 * the msg is show by msg-view is added to the new model. */
1244 priv->row_inserted_handler =
1245 g_signal_connect (priv->header_model, "row-inserted",
1246 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1249 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1250 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1254 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1256 ModestMsgViewWindowPrivate *priv= NULL;
1258 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1259 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1261 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1265 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1267 ModestMsgViewWindowPrivate *priv= NULL;
1269 TnyHeader *header = NULL;
1270 GtkTreePath *path = NULL;
1273 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1274 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1276 /* If the message was not obtained from a treemodel,
1277 * for instance if it was opened directly by the search UI:
1279 if (priv->header_model == NULL ||
1280 priv->row_reference == NULL ||
1281 !gtk_tree_row_reference_valid (priv->row_reference)) {
1282 msg = modest_msg_view_window_get_message (self);
1284 header = tny_msg_get_header (msg);
1285 g_object_unref (msg);
1290 /* Get iter of the currently selected message in the header view: */
1291 path = gtk_tree_row_reference_get_path (priv->row_reference);
1292 g_return_val_if_fail (path != NULL, NULL);
1293 gtk_tree_model_get_iter (priv->header_model,
1297 /* Get current message header */
1298 gtk_tree_model_get (priv->header_model, &iter,
1299 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1302 gtk_tree_path_free (path);
1307 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1309 ModestMsgViewWindowPrivate *priv;
1311 g_return_val_if_fail (self, NULL);
1313 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1315 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1319 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1321 ModestMsgViewWindowPrivate *priv;
1323 g_return_val_if_fail (self, NULL);
1325 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1327 return (const gchar*) priv->msg_uid;
1331 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1334 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1335 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1336 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1340 is_active = gtk_toggle_action_get_active (toggle);
1343 gtk_widget_show (priv->find_toolbar);
1344 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1346 gtk_widget_hide (priv->find_toolbar);
1349 /* update the toggle buttons status */
1350 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1351 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1352 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1353 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1358 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1359 ModestMsgViewWindow *obj)
1361 GtkToggleAction *toggle;
1362 ModestWindowPrivate *parent_priv;
1363 ModestMsgViewWindowPrivate *priv;
1365 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1366 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1368 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1369 gtk_toggle_action_set_active (toggle, FALSE);
1370 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1374 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1375 ModestMsgViewWindow *obj)
1377 gchar *current_search;
1378 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1380 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1381 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1385 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1387 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1388 g_free (current_search);
1389 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1393 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1395 g_free (priv->last_search);
1396 priv->last_search = g_strdup (current_search);
1397 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1400 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1401 g_free (priv->last_search);
1402 priv->last_search = NULL;
1404 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1405 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1408 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1409 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1410 g_free (priv->last_search);
1411 priv->last_search = NULL;
1413 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1414 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1418 g_free (current_search);
1423 modest_msg_view_window_set_zoom (ModestWindow *window,
1426 ModestMsgViewWindowPrivate *priv;
1427 ModestWindowPrivate *parent_priv;
1428 GtkAction *action = NULL;
1429 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1431 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1433 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1434 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1435 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1437 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1438 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1440 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1444 modest_msg_view_window_get_zoom (ModestWindow *window)
1446 ModestMsgViewWindowPrivate *priv;
1448 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1450 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1451 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1455 modest_msg_view_window_zoom_plus (ModestWindow *window)
1457 ModestWindowPrivate *parent_priv;
1458 GtkRadioAction *zoom_radio_action;
1459 GSList *group, *node;
1461 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1462 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1463 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1465 group = gtk_radio_action_get_group (zoom_radio_action);
1467 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1468 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1472 for (node = group; node != NULL; node = g_slist_next (node)) {
1473 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1474 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1482 modest_msg_view_window_zoom_minus (ModestWindow *window)
1484 ModestWindowPrivate *parent_priv;
1485 GtkRadioAction *zoom_radio_action;
1486 GSList *group, *node;
1488 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1489 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1490 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1492 group = gtk_radio_action_get_group (zoom_radio_action);
1494 for (node = group; node != NULL; node = g_slist_next (node)) {
1495 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1496 if (node->next != NULL) {
1497 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1500 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1510 modest_msg_view_window_key_event (GtkWidget *window,
1516 focus = gtk_window_get_focus (GTK_WINDOW (window));
1518 /* for the find toolbar case */
1519 if (focus && GTK_IS_ENTRY (focus)) {
1520 if (event->keyval == GDK_BackSpace) {
1522 copy = gdk_event_copy ((GdkEvent *) event);
1523 gtk_widget_event (focus, copy);
1524 gdk_event_free (copy);
1529 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1530 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1531 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1532 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1533 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1534 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1535 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1536 gboolean return_value;
1538 if (event->type == GDK_KEY_RELEASE) {
1539 GtkScrollType scroll_type;
1541 switch (event->keyval) {
1544 scroll_type = GTK_SCROLL_STEP_UP; break;
1547 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1549 case GDK_KP_Page_Up:
1550 scroll_type = GTK_SCROLL_PAGE_UP; break;
1552 case GDK_KP_Page_Down:
1553 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1556 scroll_type = GTK_SCROLL_START; break;
1559 scroll_type = GTK_SCROLL_END; break;
1560 default: scroll_type = GTK_SCROLL_NONE;
1563 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",
1564 scroll_type, FALSE, &return_value);
1575 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1578 ModestMsgViewWindowPrivate *priv;
1579 GtkTreeIter tmp_iter;
1580 gboolean is_last_selected;
1582 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1583 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1585 /*if no model (so no rows at all), then virtually we are the last*/
1586 if (!priv->header_model || !priv->row_reference)
1589 path = gtk_tree_row_reference_get_path (priv->row_reference);
1593 is_last_selected = TRUE;
1594 while (is_last_selected) {
1596 gtk_tree_path_next (path);
1597 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1599 gtk_tree_model_get (priv->header_model, &tmp_iter,
1600 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1603 if (msg_is_visible (header, priv->is_outbox))
1604 is_last_selected = FALSE;
1605 g_object_unref(G_OBJECT(header));
1608 gtk_tree_path_free (path);
1609 return is_last_selected;
1613 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1615 ModestMsgViewWindowPrivate *priv;
1617 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1618 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1620 return priv->header_model != NULL;
1624 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1626 ModestMsgViewWindowPrivate *priv;
1628 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1629 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1631 return priv->is_search_result;
1635 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1637 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1639 if (!check_outbox) {
1642 ModestTnySendQueueStatus status;
1643 status = modest_tny_all_send_queues_get_msg_status (header);
1644 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1645 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1650 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1653 ModestMsgViewWindowPrivate *priv;
1654 gboolean is_first_selected;
1655 GtkTreeIter tmp_iter;
1656 /* gchar * path_string;*/
1658 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1659 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1661 /*if no model (so no rows at all), then virtually we are the first*/
1662 if (!priv->header_model || !priv->row_reference)
1665 path = gtk_tree_row_reference_get_path (priv->row_reference);
1669 /* path_string = gtk_tree_path_to_string (path);
1670 is_first_selected = strcmp (path_string, "0");
1672 g_free (path_string);
1673 gtk_tree_path_free (path);
1675 return is_first_selected;*/
1677 is_first_selected = TRUE;
1678 while (is_first_selected) {
1680 if(!gtk_tree_path_prev (path))
1682 /* Here the 'if' is needless for logic, but let make sure
1683 * iter is valid for gtk_tree_model_get. */
1684 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1686 gtk_tree_model_get (priv->header_model, &tmp_iter,
1687 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1690 if (msg_is_visible (header, priv->is_outbox))
1691 is_first_selected = FALSE;
1692 g_object_unref(G_OBJECT(header));
1695 gtk_tree_path_free (path);
1696 return is_first_selected;
1701 GtkTreeRowReference *row_reference;
1705 message_reader_performer (gboolean canceled,
1707 GtkWindow *parent_window,
1708 TnyAccount *account,
1711 ModestMailOperation *mail_op = NULL;
1712 MsgReaderInfo *info;
1714 info = (MsgReaderInfo *) user_data;
1715 if (canceled || err) {
1719 /* Register the header - it'll be unregistered in the callback */
1720 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1722 /* New mail operation */
1723 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1724 modest_ui_actions_disk_operations_error_handler,
1727 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1728 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1729 g_object_unref (mail_op);
1731 /* Update dimming rules */
1732 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1733 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1736 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1737 g_object_unref (info->header);
1738 g_slice_free (MsgReaderInfo, info);
1743 * Reads the message whose summary item is @header. It takes care of
1744 * several things, among others:
1746 * If the message was not previously downloaded then ask the user
1747 * before downloading. If there is no connection launch the connection
1748 * dialog. Update toolbar dimming rules.
1750 * Returns: TRUE if the mail operation was started, otherwise if the
1751 * user do not want to download the message, or if the user do not
1752 * want to connect, then the operation is not issued
1755 message_reader (ModestMsgViewWindow *window,
1756 ModestMsgViewWindowPrivate *priv,
1758 GtkTreeRowReference *row_reference)
1760 gboolean already_showing = FALSE;
1761 ModestWindow *msg_window = NULL;
1762 ModestWindowMgr *mgr;
1763 TnyAccount *account;
1765 MsgReaderInfo *info;
1767 g_return_val_if_fail (row_reference != NULL, FALSE);
1769 mgr = modest_runtime_get_window_mgr ();
1770 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1771 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1774 gtk_window_present (GTK_WINDOW (msg_window));
1775 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1779 /* Msg download completed */
1780 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1781 /* Ask the user if he wants to download the message if
1783 if (!tny_device_is_online (modest_runtime_get_device())) {
1784 GtkResponseType response;
1786 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1787 _("mcen_nc_get_msg"));
1788 if (response == GTK_RESPONSE_CANCEL)
1791 folder = tny_header_get_folder (header);
1792 info = g_slice_new (MsgReaderInfo);
1793 info->header = g_object_ref (header);
1794 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1796 /* Offer the connection dialog if necessary */
1797 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1799 TNY_FOLDER_STORE (folder),
1800 message_reader_performer,
1802 g_object_unref (folder);
1807 folder = tny_header_get_folder (header);
1808 account = tny_folder_get_account (folder);
1809 info = g_slice_new (MsgReaderInfo);
1810 info->header = g_object_ref (header);
1811 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1813 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1814 g_object_unref (account);
1815 g_object_unref (folder);
1821 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1823 ModestMsgViewWindowPrivate *priv;
1824 GtkTreePath *path= NULL;
1825 GtkTreeIter tmp_iter;
1827 gboolean retval = TRUE;
1828 GtkTreeRowReference *row_reference = NULL;
1830 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1831 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1833 if (!priv->row_reference)
1836 /* Update the next row reference if it's not valid. This could
1837 happen if for example the header which it was pointing to,
1838 was deleted. The best place to do it is in the row-deleted
1839 handler but the tinymail model do not work like the glib
1840 tree models and reports the deletion when the row is still
1842 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1843 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1844 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1845 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1848 if (priv->next_row_reference)
1849 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1853 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1855 gtk_tree_model_get_iter (priv->header_model,
1858 gtk_tree_path_free (path);
1860 gtk_tree_model_get (priv->header_model, &tmp_iter,
1861 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1864 /* Read the message & show it */
1865 if (!message_reader (window, priv, header, row_reference)) {
1868 gtk_tree_row_reference_free (row_reference);
1871 g_object_unref (header);
1877 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1879 ModestMsgViewWindowPrivate *priv = NULL;
1881 gboolean finished = FALSE;
1882 gboolean retval = FALSE;
1884 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1885 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1887 /* Return inmediatly if there is no header model */
1888 if (!priv->header_model || !priv->row_reference)
1891 path = gtk_tree_row_reference_get_path (priv->row_reference);
1892 while (!finished && gtk_tree_path_prev (path)) {
1896 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1897 gtk_tree_model_get (priv->header_model, &iter,
1898 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1902 if (msg_is_visible (header, priv->is_outbox)) {
1903 GtkTreeRowReference *row_reference;
1904 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1905 /* Read the message & show it */
1906 retval = message_reader (window, priv, header, row_reference);
1907 gtk_tree_row_reference_free (row_reference);
1911 g_object_unref (header);
1915 gtk_tree_path_free (path);
1920 view_msg_cb (ModestMailOperation *mail_op,
1927 ModestMsgViewWindow *self = NULL;
1928 ModestMsgViewWindowPrivate *priv = NULL;
1929 GtkTreeRowReference *row_reference = NULL;
1931 /* Unregister the header (it was registered before creating the mail operation) */
1932 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1934 row_reference = (GtkTreeRowReference *) user_data;
1936 gtk_tree_row_reference_free (row_reference);
1940 /* If there was any error */
1941 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1942 gtk_tree_row_reference_free (row_reference);
1946 /* Get the window */
1947 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1948 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1949 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1951 /* Update the row reference */
1952 if (priv->row_reference != NULL) {
1953 gtk_tree_row_reference_free (priv->row_reference);
1954 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1955 if (priv->next_row_reference != NULL) {
1956 gtk_tree_row_reference_free (priv->next_row_reference);
1958 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1959 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1962 /* Mark header as read */
1963 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1964 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1966 /* Set new message */
1967 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1968 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1969 modest_msg_view_window_update_priority (self);
1970 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1971 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1974 /* Set the new message uid of the window */
1975 if (priv->msg_uid) {
1976 g_free (priv->msg_uid);
1977 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1980 /* Notify the observers */
1981 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1982 0, priv->header_model, priv->row_reference);
1985 g_object_unref (self);
1986 gtk_tree_row_reference_free (row_reference);
1990 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1992 ModestMsgViewWindowPrivate *priv;
1994 TnyFolderType folder_type;
1996 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1998 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2000 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2004 folder = tny_msg_get_folder (msg);
2006 folder_type = modest_tny_folder_guess_folder_type (folder);
2007 g_object_unref (folder);
2009 g_object_unref (msg);
2017 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2019 ModestMsgViewWindowPrivate *priv;
2020 TnyHeader *header = NULL;
2021 TnyHeaderFlags flags = 0;
2023 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2025 if (priv->header_model && priv->row_reference) {
2027 GtkTreePath *path = NULL;
2029 path = gtk_tree_row_reference_get_path (priv->row_reference);
2030 g_return_if_fail (path != NULL);
2031 gtk_tree_model_get_iter (priv->header_model,
2033 gtk_tree_row_reference_get_path (priv->row_reference));
2035 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2037 gtk_tree_path_free (path);
2040 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2042 header = tny_msg_get_header (msg);
2043 g_object_unref (msg);
2048 flags = tny_header_get_flags (header);
2049 g_object_unref(G_OBJECT(header));
2052 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2057 toolbar_resize (ModestMsgViewWindow *self)
2059 ModestMsgViewWindowPrivate *priv = NULL;
2060 ModestWindowPrivate *parent_priv = NULL;
2062 gint static_button_size;
2063 ModestWindowMgr *mgr;
2065 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2066 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2067 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2069 mgr = modest_runtime_get_window_mgr ();
2070 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2072 if (parent_priv->toolbar) {
2073 /* left size buttons */
2074 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2075 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2076 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2077 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2078 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2079 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2080 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2081 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2082 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2083 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2084 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2085 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2086 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2087 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2088 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2089 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2091 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2092 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2093 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2094 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2095 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2096 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2097 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2098 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2104 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2106 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2107 ModestWindowPrivate *parent_priv;
2108 ModestWindowMgr *mgr;
2109 gboolean is_fullscreen;
2110 GtkAction *fs_toggle_action;
2113 mgr = modest_runtime_get_window_mgr ();
2114 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2116 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2118 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2119 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2120 if (is_fullscreen != active) {
2121 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2123 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2131 modest_msg_view_window_show_toolbar (ModestWindow *self,
2132 gboolean show_toolbar)
2134 ModestMsgViewWindowPrivate *priv = NULL;
2135 ModestWindowPrivate *parent_priv;
2136 GtkWidget *reply_button = NULL, *menu = NULL;
2137 GtkWidget *placeholder = NULL;
2139 const gchar *action_name;
2142 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2143 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2145 /* Set optimized view status */
2146 priv->optimized_view = !show_toolbar;
2148 if (!parent_priv->toolbar) {
2149 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2151 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2153 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2154 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2155 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2156 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2157 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2159 /* Add ProgressBar (Transfer toolbar) */
2160 priv->progress_bar = modest_progress_bar_new ();
2161 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2162 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2163 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2164 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2165 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2167 /* Connect cancel 'clicked' signal to abort progress mode */
2168 g_signal_connect(priv->cancel_toolitem, "clicked",
2169 G_CALLBACK(cancel_progressbar),
2172 /* Add it to the observers list */
2173 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2176 hildon_window_add_toolbar (HILDON_WINDOW (self),
2177 GTK_TOOLBAR (parent_priv->toolbar));
2179 /* Set reply button tap and hold menu */
2180 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2181 "/ToolBar/ToolbarMessageReply");
2182 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2183 "/ToolbarReplyCSM");
2184 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2188 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2189 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2190 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2192 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2193 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2194 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2196 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2199 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2200 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2203 /* Update also the actions (to update the toggles in the
2204 menus), we have to do it manually because some other window
2205 of the same time could have changed it (remember that the
2206 toolbar fullscreen mode is shared by all the windows of the
2208 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2209 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2211 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2213 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2214 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2219 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2221 ModestMsgViewWindow *window)
2223 if (!GTK_WIDGET_VISIBLE (window))
2226 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2230 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2232 ModestMsgViewWindowPrivate *priv;
2234 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2235 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2237 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2241 cancel_progressbar (GtkToolButton *toolbutton,
2242 ModestMsgViewWindow *self)
2245 ModestMsgViewWindowPrivate *priv;
2247 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2249 /* Get operation observers and cancel its current operation */
2250 tmp = priv->progress_widgets;
2252 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2253 tmp=g_slist_next(tmp);
2257 observers_empty (ModestMsgViewWindow *self)
2260 ModestMsgViewWindowPrivate *priv;
2261 gboolean is_empty = TRUE;
2262 guint pending_ops = 0;
2264 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2265 tmp = priv->progress_widgets;
2267 /* Check all observers */
2268 while (tmp && is_empty) {
2269 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2270 is_empty = pending_ops == 0;
2272 tmp = g_slist_next(tmp);
2279 on_account_removed (TnyAccountStore *account_store,
2280 TnyAccount *account,
2283 /* Do nothing if it's a transport account, because we only
2284 show the messages of a store account */
2285 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2286 const gchar *parent_acc = NULL;
2287 const gchar *our_acc = NULL;
2289 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2290 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2292 /* Close this window if I'm showing a message of the removed account */
2293 if (strcmp (parent_acc, our_acc) == 0)
2294 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2299 on_mail_operation_started (ModestMailOperation *mail_op,
2302 ModestMsgViewWindow *self;
2303 ModestMailOperationTypeOperation op_type;
2305 ModestMsgViewWindowPrivate *priv;
2306 GObject *source = NULL;
2308 self = MODEST_MSG_VIEW_WINDOW (user_data);
2309 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2310 op_type = modest_mail_operation_get_type_operation (mail_op);
2311 tmp = priv->progress_widgets;
2312 source = modest_mail_operation_get_source(mail_op);
2313 if (G_OBJECT (self) == source) {
2314 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2315 set_toolbar_transfer_mode(self);
2317 modest_progress_object_add_operation (
2318 MODEST_PROGRESS_OBJECT (tmp->data),
2320 tmp = g_slist_next (tmp);
2324 g_object_unref (source);
2328 on_mail_operation_finished (ModestMailOperation *mail_op,
2331 ModestMsgViewWindow *self;
2332 ModestMailOperationTypeOperation op_type;
2334 ModestMsgViewWindowPrivate *priv;
2336 self = MODEST_MSG_VIEW_WINDOW (user_data);
2337 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2338 op_type = modest_mail_operation_get_type_operation (mail_op);
2339 tmp = priv->progress_widgets;
2341 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2343 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2345 tmp = g_slist_next (tmp);
2348 /* If no more operations are being observed, NORMAL mode is enabled again */
2349 if (observers_empty (self)) {
2350 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2353 /* Update dimming rules. We have to do this right here
2354 and not in view_msg_cb because at that point the
2355 transfer mode is still enabled so the dimming rule
2356 won't let the user delete the message that has been
2357 readed for example */
2358 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2359 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2364 on_queue_changed (ModestMailOperationQueue *queue,
2365 ModestMailOperation *mail_op,
2366 ModestMailOperationQueueNotification type,
2367 ModestMsgViewWindow *self)
2369 ModestMsgViewWindowPrivate *priv;
2371 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2373 /* If this operations was created by another window, do nothing */
2374 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2377 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2378 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2380 "operation-started",
2381 G_CALLBACK (on_mail_operation_started),
2383 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2385 "operation-finished",
2386 G_CALLBACK (on_mail_operation_finished),
2388 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2389 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2391 "operation-started");
2392 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2394 "operation-finished");
2399 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2401 ModestMsgViewWindowPrivate *priv;
2402 TnyList *selected_attachments = NULL;
2404 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2405 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2407 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2409 return selected_attachments;
2413 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2415 ModestMsgViewWindowPrivate *priv;
2416 const gchar *msg_uid;
2417 gchar *attachment_uid = NULL;
2418 gint attachment_index = 0;
2419 TnyList *attachments;
2421 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2422 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2423 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2425 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2426 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2427 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2428 g_object_unref (attachments);
2430 if (msg_uid && attachment_index >= 0) {
2431 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2434 if (mime_part == NULL) {
2435 gboolean error = FALSE;
2436 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2437 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2439 } else if (tny_list_get_length (selected_attachments) > 1) {
2440 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2444 iter = tny_list_create_iterator (selected_attachments);
2445 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2446 g_object_unref (iter);
2448 g_object_unref (selected_attachments);
2453 g_object_ref (mime_part);
2456 if (tny_mime_part_is_purged (mime_part)) {
2457 g_object_unref (mime_part);
2461 if (!modest_tny_mime_part_is_msg (mime_part)) {
2462 gchar *filepath = NULL;
2463 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2464 const gchar *content_type;
2465 gboolean show_error_banner = FALSE;
2467 TnyFsStream *temp_stream = NULL;
2468 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2471 if (temp_stream != NULL) {
2472 content_type = tny_mime_part_get_content_type (mime_part);
2473 if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2474 /* make the file read-only */
2475 if (g_chmod(filepath, 0444) != 0)
2476 g_warning ("%s: failed to set file '%s' to read-only: %s",
2477 __FUNCTION__, filepath, strerror(errno));
2479 modest_platform_activate_file (filepath, content_type);
2481 /* error while saving attachment, maybe cerm_device_memory_full */
2482 show_error_banner = TRUE;
2484 g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2488 g_object_unref (temp_stream);
2490 /* NOTE: files in the temporary area will be automatically
2491 * cleaned after some time if they are no longer in use */
2493 if (filepath != NULL) {
2494 /* the file may already exist but it isn't writable,
2495 * let's try to open it anyway */
2496 content_type = tny_mime_part_get_content_type (mime_part);
2497 modest_platform_activate_file (filepath, content_type);
2500 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2501 show_error_banner = TRUE;
2504 if (show_error_banner)
2505 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2507 /* message attachment */
2508 TnyHeader *header = NULL;
2509 ModestWindowMgr *mgr;
2510 ModestWindow *msg_win = NULL;
2513 header = tny_msg_get_header (TNY_MSG (mime_part));
2514 mgr = modest_runtime_get_window_mgr ();
2515 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2518 if (msg_win) /* there is already a window for this uid; top it */
2519 gtk_window_present (GTK_WINDOW(msg_win));
2521 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2522 * thus, we don't do anything */
2523 g_warning ("window for is already being created");
2525 /* it's not found, so create a new window for it */
2526 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2527 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2529 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2530 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2531 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2532 modest_window_get_zoom (MODEST_WINDOW (window)));
2533 modest_window_mgr_register_window (mgr, msg_win);
2534 gtk_widget_show_all (GTK_WIDGET (msg_win));
2537 g_object_unref (mime_part);
2550 GnomeVFSResult result;
2553 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2554 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2555 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2556 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2559 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2563 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2564 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2565 g_free (pair->filename);
2566 g_object_unref (pair->part);
2567 g_slice_free (SaveMimePartPair, pair);
2569 g_list_free (info->pairs);
2572 gtk_widget_destroy (info->banner);
2573 g_slice_free (SaveMimePartInfo, info);
2578 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2580 if (info->pairs != NULL) {
2581 save_mime_part_to_file (info);
2583 /* This is a GDK lock because we are an idle callback and
2584 * hildon_banner_show_information is or does Gtk+ code */
2586 gdk_threads_enter (); /* CHECKED */
2587 save_mime_part_info_free (info, TRUE);
2588 if (info->result == GNOME_VFS_OK) {
2589 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2590 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2591 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2592 "cerm_device_memory_full"));
2594 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2596 gdk_threads_leave (); /* CHECKED */
2603 save_mime_part_to_file (SaveMimePartInfo *info)
2605 GnomeVFSHandle *handle;
2607 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2609 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2610 if (info->result == GNOME_VFS_OK) {
2611 stream = tny_vfs_stream_new (handle);
2612 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2613 info->result = GNOME_VFS_ERROR_IO;
2615 g_object_unref (G_OBJECT (stream));
2616 g_object_unref (pair->part);
2617 g_slice_free (SaveMimePartPair, pair);
2618 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2620 save_mime_part_info_free (info, FALSE);
2623 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2628 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2630 gboolean is_ok = TRUE;
2631 gint replaced_files = 0;
2632 const GList *files = info->pairs;
2635 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2636 SaveMimePartPair *pair = iter->data;
2637 if (modest_utils_file_exists (pair->filename)) {
2641 if (replaced_files) {
2642 GtkWidget *confirm_overwrite_dialog;
2643 const gchar *message = (replaced_files == 1) ?
2644 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2645 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2646 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2649 gtk_widget_destroy (confirm_overwrite_dialog);
2653 save_mime_part_info_free (info, TRUE);
2655 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2656 _CS("sfil_ib_saving"));
2657 info->banner = banner;
2658 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2665 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2667 ModestMsgViewWindowPrivate *priv;
2668 GList *files_to_save = NULL;
2669 GtkWidget *save_dialog = NULL;
2670 gchar *folder = NULL;
2671 const gchar *filename = NULL;
2672 gchar *save_multiple_str = NULL;
2674 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2675 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2677 if (mime_parts == NULL) {
2678 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2679 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2682 g_object_ref (mime_parts);
2685 /* prepare dialog */
2686 if (tny_list_get_length (mime_parts) == 1) {
2688 /* only one attachment selected */
2689 iter = tny_list_create_iterator (mime_parts);
2690 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2691 g_object_unref (iter);
2692 if (!modest_tny_mime_part_is_msg (mime_part) &&
2693 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2694 !tny_mime_part_is_purged (mime_part)) {
2695 filename = tny_mime_part_get_filename (mime_part);
2697 /* TODO: show any error? */
2698 g_warning ("Tried to save a non-file attachment");
2699 g_object_unref (mime_parts);
2702 g_object_unref (mime_part);
2704 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2705 tny_list_get_length (mime_parts));
2708 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2709 GTK_FILE_CHOOSER_ACTION_SAVE);
2712 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2713 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2717 if (filename != NULL)
2718 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2721 /* if multiple, set multiple string */
2722 if (save_multiple_str) {
2723 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2724 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2728 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2729 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2731 if (!modest_utils_folder_writable (chooser_uri)) {
2732 hildon_banner_show_information
2733 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2737 iter = tny_list_create_iterator (mime_parts);
2738 while (!tny_iterator_is_done (iter)) {
2739 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2741 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2742 !tny_mime_part_is_purged (mime_part) &&
2743 (tny_mime_part_get_filename (mime_part) != NULL)) {
2744 SaveMimePartPair *pair;
2746 pair = g_slice_new0 (SaveMimePartPair);
2747 if (save_multiple_str) {
2748 gchar *escaped = gnome_vfs_escape_slashes (
2749 tny_mime_part_get_filename (mime_part));
2750 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2753 pair->filename = g_strdup (chooser_uri);
2755 pair->part = mime_part;
2756 files_to_save = g_list_prepend (files_to_save, pair);
2758 tny_iterator_next (iter);
2760 g_object_unref (iter);
2762 g_free (chooser_uri);
2765 gtk_widget_destroy (save_dialog);
2767 g_object_unref (mime_parts);
2769 if (files_to_save != NULL) {
2770 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2771 info->pairs = files_to_save;
2772 info->result = TRUE;
2773 save_mime_parts_to_file_with_checks (info);
2778 show_remove_attachment_information (gpointer userdata)
2780 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2781 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2783 /* We're outside the main lock */
2784 gdk_threads_enter ();
2786 if (priv->remove_attachment_banner != NULL) {
2787 gtk_widget_destroy (priv->remove_attachment_banner);
2788 g_object_unref (priv->remove_attachment_banner);
2791 priv->remove_attachment_banner = g_object_ref (
2792 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2794 gdk_threads_leave ();
2800 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2802 ModestMsgViewWindowPrivate *priv;
2803 TnyList *mime_parts = NULL;
2804 gchar *confirmation_message;
2809 /* TnyFolder *folder; */
2811 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2812 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2815 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2817 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2819 /* Remove already purged messages from mime parts list */
2820 iter = tny_list_create_iterator (mime_parts);
2821 while (!tny_iterator_is_done (iter)) {
2822 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2823 tny_iterator_next (iter);
2824 if (tny_mime_part_is_purged (part)) {
2825 tny_list_remove (mime_parts, (GObject *) part);
2827 g_object_unref (part);
2829 g_object_unref (iter);
2831 if (tny_list_get_length (mime_parts) == 0) {
2832 g_object_unref (mime_parts);
2836 n_attachments = tny_list_get_length (mime_parts);
2837 if (n_attachments == 1) {
2841 iter = tny_list_create_iterator (mime_parts);
2842 part = (TnyMimePart *) tny_iterator_get_current (iter);
2843 g_object_unref (iter);
2844 if (modest_tny_mime_part_is_msg (part)) {
2846 header = tny_msg_get_header (TNY_MSG (part));
2847 filename = tny_header_dup_subject (header);
2848 g_object_unref (header);
2849 if (filename == NULL)
2850 filename = g_strdup (_("mail_va_no_subject"));
2852 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2854 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2856 g_object_unref (part);
2858 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2859 "mcen_nc_purge_files_text",
2860 n_attachments), n_attachments);
2862 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2863 confirmation_message);
2864 g_free (confirmation_message);
2866 if (response != GTK_RESPONSE_OK) {
2867 g_object_unref (mime_parts);
2871 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2872 /* folder = tny_msg_get_folder (msg); */
2873 /* tny_msg_uncache_attachments (msg); */
2874 /* tny_folder_refresh (folder, NULL); */
2875 /* g_object_unref (folder); */
2877 iter = tny_list_create_iterator (mime_parts);
2878 while (!tny_iterator_is_done (iter)) {
2881 part = (TnyMimePart *) tny_iterator_get_current (iter);
2882 tny_mime_part_set_purged (TNY_MIME_PART (part));
2883 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2884 g_object_unref (part);
2885 tny_iterator_next (iter);
2887 g_object_unref (iter);
2889 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2890 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2891 tny_msg_rewrite_cache (msg);
2892 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2893 g_object_unref (msg);
2895 g_object_unref (mime_parts);
2897 if (priv->purge_timeout > 0) {
2898 g_source_remove (priv->purge_timeout);
2899 priv->purge_timeout = 0;
2902 if (priv->remove_attachment_banner) {
2903 gtk_widget_destroy (priv->remove_attachment_banner);
2904 g_object_unref (priv->remove_attachment_banner);
2905 priv->remove_attachment_banner = NULL;
2913 update_window_title (ModestMsgViewWindow *window)
2915 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2917 TnyHeader *header = NULL;
2918 gchar *subject = NULL;
2920 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2923 header = tny_msg_get_header (msg);
2924 subject = tny_header_dup_subject (header);
2925 g_object_unref (header);
2926 g_object_unref (msg);
2929 if ((subject == NULL)||(subject[0] == '\0')) {
2931 subject = g_strdup (_("mail_va_no_subject"));
2934 gtk_window_set_title (GTK_WINDOW (window), subject);
2938 static void on_move_focus (GtkWidget *widget,
2939 GtkDirectionType direction,
2942 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");