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);
98 void modest_msg_view_window_on_row_changed(
99 GtkTreeModel *header_model,
102 ModestMsgViewWindow *window);
104 void modest_msg_view_window_on_row_deleted(
105 GtkTreeModel *header_model,
107 ModestMsgViewWindow *window);
109 void modest_msg_view_window_on_row_inserted(
110 GtkTreeModel *header_model,
111 GtkTreePath *tree_path,
112 GtkTreeIter *tree_iter,
113 ModestMsgViewWindow *window);
115 void modest_msg_view_window_on_row_reordered(
116 GtkTreeModel *header_model,
120 ModestMsgViewWindow *window);
122 void modest_msg_view_window_update_model_replaced(
123 ModestHeaderViewObserver *window,
125 const gchar *tny_folder_id);
127 static void cancel_progressbar (GtkToolButton *toolbutton,
128 ModestMsgViewWindow *self);
130 static void on_queue_changed (ModestMailOperationQueue *queue,
131 ModestMailOperation *mail_op,
132 ModestMailOperationQueueNotification type,
133 ModestMsgViewWindow *self);
135 static void on_account_removed (TnyAccountStore *account_store,
139 static void on_move_focus (GtkWidget *widget,
140 GtkDirectionType direction,
143 static void view_msg_cb (ModestMailOperation *mail_op,
150 static void set_toolbar_mode (ModestMsgViewWindow *self,
151 ModestToolBarModes mode);
153 static void update_window_title (ModestMsgViewWindow *window);
155 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
156 static void init_window (ModestMsgViewWindow *obj);
158 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
161 /* list my signals */
167 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
168 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
169 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
172 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
173 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
174 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
175 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
176 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
177 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
178 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
181 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
182 struct _ModestMsgViewWindowPrivate {
185 GtkWidget *main_scroll;
186 GtkWidget *find_toolbar;
189 /* Progress observers */
190 GtkWidget *progress_bar;
191 GSList *progress_widgets;
194 GtkWidget *progress_toolitem;
195 GtkWidget *cancel_toolitem;
196 GtkWidget *prev_toolitem;
197 GtkWidget *next_toolitem;
198 ModestToolBarModes current_toolbar_mode;
200 /* Optimized view enabled */
201 gboolean optimized_view;
203 /* Whether this was created via the *_new_for_search_result() function. */
204 gboolean is_search_result;
206 /* Whether the message is in outbox */
209 /* A reference to the @model of the header view
210 * to allow selecting previous/next messages,
211 * if the message is currently selected in the header view.
213 const gchar *header_folder_id;
214 GtkTreeModel *header_model;
215 GtkTreeRowReference *row_reference;
216 GtkTreeRowReference *next_row_reference;
218 gulong clipboard_change_handler;
219 gulong queue_change_handler;
220 gulong account_removed_handler;
221 gulong row_changed_handler;
222 gulong row_deleted_handler;
223 gulong row_inserted_handler;
224 gulong rows_reordered_handler;
227 GtkWidget *remove_attachment_banner;
229 guint progress_bar_timeout;
236 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
237 MODEST_TYPE_MSG_VIEW_WINDOW, \
238 ModestMsgViewWindowPrivate))
240 static GtkWindowClass *parent_class = NULL;
242 /* uncomment the following if you have defined any signals */
243 static guint signals[LAST_SIGNAL] = {0};
246 modest_msg_view_window_get_type (void)
248 static GType my_type = 0;
250 static const GTypeInfo my_info = {
251 sizeof(ModestMsgViewWindowClass),
252 NULL, /* base init */
253 NULL, /* base finalize */
254 (GClassInitFunc) modest_msg_view_window_class_init,
255 NULL, /* class finalize */
256 NULL, /* class data */
257 sizeof(ModestMsgViewWindow),
259 (GInstanceInitFunc) modest_msg_view_window_init,
262 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
263 "ModestMsgViewWindow",
266 static const GInterfaceInfo modest_header_view_observer_info =
268 (GInterfaceInitFunc) modest_header_view_observer_init,
269 NULL, /* interface_finalize */
270 NULL /* interface_data */
273 g_type_add_interface_static (my_type,
274 MODEST_TYPE_HEADER_VIEW_OBSERVER,
275 &modest_header_view_observer_info);
281 save_state (ModestWindow *self)
283 modest_widget_memory_save (modest_runtime_get_conf (),
285 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
290 restore_settings (ModestMsgViewWindow *self)
293 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
296 conf = modest_runtime_get_conf ();
297 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
298 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
299 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
300 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
301 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
302 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
303 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
304 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
305 modest_widget_memory_restore (conf,
307 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
311 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
313 GObjectClass *gobject_class;
314 ModestWindowClass *modest_window_class;
315 gobject_class = (GObjectClass*) klass;
316 modest_window_class = (ModestWindowClass *) klass;
318 parent_class = g_type_class_peek_parent (klass);
319 gobject_class->finalize = modest_msg_view_window_finalize;
321 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
322 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
323 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
324 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
325 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
326 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
328 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
330 modest_window_class->save_state_func = save_state;
332 signals[MSG_CHANGED_SIGNAL] =
333 g_signal_new ("msg-changed",
334 G_TYPE_FROM_CLASS (gobject_class),
336 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
338 modest_marshal_VOID__POINTER_POINTER,
339 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
342 static void modest_header_view_observer_init(
343 ModestHeaderViewObserverIface *iface_class)
345 iface_class->update_func = modest_msg_view_window_update_model_replaced;
349 modest_msg_view_window_init (ModestMsgViewWindow *obj)
351 ModestMsgViewWindowPrivate *priv;
352 ModestWindowPrivate *parent_priv = NULL;
353 GtkActionGroup *action_group = NULL;
354 GError *error = NULL;
355 GdkPixbuf *window_icon;
357 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
358 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
359 parent_priv->ui_manager = gtk_ui_manager_new();
361 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
362 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
364 /* Add common actions */
365 gtk_action_group_add_actions (action_group,
366 modest_action_entries,
367 G_N_ELEMENTS (modest_action_entries),
369 gtk_action_group_add_toggle_actions (action_group,
370 modest_toggle_action_entries,
371 G_N_ELEMENTS (modest_toggle_action_entries),
373 gtk_action_group_add_toggle_actions (action_group,
374 msg_view_toggle_action_entries,
375 G_N_ELEMENTS (msg_view_toggle_action_entries),
377 gtk_action_group_add_radio_actions (action_group,
378 msg_view_zoom_action_entries,
379 G_N_ELEMENTS (msg_view_zoom_action_entries),
381 G_CALLBACK (modest_ui_actions_on_change_zoom),
384 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
385 g_object_unref (action_group);
387 /* Load the UI definition */
388 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
391 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
392 g_error_free (error);
397 /* Add accelerators */
398 gtk_window_add_accel_group (GTK_WINDOW (obj),
399 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
401 priv->is_search_result = FALSE;
402 priv->is_outbox = FALSE;
404 priv->msg_view = NULL;
405 priv->header_model = NULL;
406 priv->header_folder_id = NULL;
407 priv->clipboard_change_handler = 0;
408 priv->queue_change_handler = 0;
409 priv->account_removed_handler = 0;
410 priv->row_changed_handler = 0;
411 priv->row_deleted_handler = 0;
412 priv->row_inserted_handler = 0;
413 priv->rows_reordered_handler = 0;
414 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
416 priv->optimized_view = FALSE;
417 priv->progress_bar_timeout = 0;
418 priv->purge_timeout = 0;
419 priv->remove_attachment_banner = NULL;
420 priv->msg_uid = NULL;
422 priv->sighandlers = NULL;
425 init_window (MODEST_MSG_VIEW_WINDOW(obj));
427 /* Set window icon */
428 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
430 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
431 g_object_unref (window_icon);
434 hildon_program_add_window (hildon_program_get_instance(),
437 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
438 GTK_WINDOW(obj),"applications_email_viewer");
443 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
445 ModestMsgViewWindowPrivate *priv = NULL;
447 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
449 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
451 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
453 if (priv->progress_bar_timeout > 0) {
454 g_source_remove (priv->progress_bar_timeout);
455 priv->progress_bar_timeout = 0;
462 set_toolbar_mode (ModestMsgViewWindow *self,
463 ModestToolBarModes mode)
465 ModestWindowPrivate *parent_priv;
466 ModestMsgViewWindowPrivate *priv;
467 /* GtkWidget *widget = NULL; */
469 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
471 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
472 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
474 /* Sets current toolbar mode */
475 priv->current_toolbar_mode = mode;
477 /* Update toolbar dimming state */
478 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
481 case TOOLBAR_MODE_NORMAL:
482 if (priv->progress_toolitem) {
483 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
484 gtk_widget_hide (priv->progress_toolitem);
487 if (priv->progress_bar)
488 gtk_widget_hide (priv->progress_bar);
490 if (priv->cancel_toolitem)
491 gtk_widget_hide (priv->cancel_toolitem);
493 if (priv->prev_toolitem)
494 gtk_widget_show (priv->prev_toolitem);
496 if (priv->next_toolitem)
497 gtk_widget_show (priv->next_toolitem);
499 /* Hide toolbar if optimized view is enabled */
500 if (priv->optimized_view) {
501 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
502 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
506 case TOOLBAR_MODE_TRANSFER:
507 if (priv->prev_toolitem)
508 gtk_widget_hide (priv->prev_toolitem);
510 if (priv->next_toolitem)
511 gtk_widget_hide (priv->next_toolitem);
513 if (priv->progress_bar)
514 gtk_widget_show (priv->progress_bar);
516 if (priv->progress_toolitem) {
517 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
518 gtk_widget_show (priv->progress_toolitem);
521 if (priv->cancel_toolitem)
522 gtk_widget_show (priv->cancel_toolitem);
524 /* Show toolbar if it's hiden (optimized view ) */
525 if (priv->optimized_view) {
526 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
527 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
532 g_return_if_reached ();
539 init_window (ModestMsgViewWindow *obj)
541 GtkWidget *main_vbox;
542 ModestMsgViewWindowPrivate *priv;
544 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
546 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
547 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
548 main_vbox = gtk_vbox_new (FALSE, 6);
550 #ifdef MODEST_USE_MOZEMBED
551 priv->main_scroll = priv->msg_view;
552 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
554 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
555 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
557 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
558 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
559 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
561 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
562 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
564 priv->find_toolbar = hildon_find_toolbar_new (NULL);
565 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
566 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
568 gtk_widget_show_all (GTK_WIDGET(main_vbox));
572 modest_msg_view_window_disconnect_signals (ModestWindow *self)
574 ModestMsgViewWindowPrivate *priv;
575 ModestHeaderView *header_view = NULL;
576 ModestWindow *main_window = NULL;
578 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
580 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
581 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
582 priv->clipboard_change_handler))
583 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
584 priv->clipboard_change_handler);
586 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
587 priv->queue_change_handler))
588 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
589 priv->queue_change_handler);
591 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
592 priv->account_removed_handler))
593 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
594 priv->account_removed_handler);
596 if (priv->header_model) {
597 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
598 priv->row_changed_handler))
599 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
600 priv->row_changed_handler);
602 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
603 priv->row_deleted_handler))
604 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
605 priv->row_deleted_handler);
607 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
608 priv->row_inserted_handler))
609 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
610 priv->row_inserted_handler);
612 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
613 priv->rows_reordered_handler))
614 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
615 priv->rows_reordered_handler);
618 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
619 priv->sighandlers = NULL;
621 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
622 FALSE); /* don't create */
626 header_view = MODEST_HEADER_VIEW(
627 modest_main_window_get_child_widget(
628 MODEST_MAIN_WINDOW(main_window),
629 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
630 if (header_view == NULL)
633 modest_header_view_remove_observer(header_view,
634 MODEST_HEADER_VIEW_OBSERVER(self));
638 modest_msg_view_window_finalize (GObject *obj)
640 ModestMsgViewWindowPrivate *priv;
642 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
644 /* Sanity check: shouldn't be needed, the window mgr should
645 call this function before */
646 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
648 if (priv->header_model != NULL) {
649 g_object_unref (priv->header_model);
650 priv->header_model = NULL;
653 if (priv->progress_bar_timeout > 0) {
654 g_source_remove (priv->progress_bar_timeout);
655 priv->progress_bar_timeout = 0;
658 if (priv->remove_attachment_banner) {
659 gtk_widget_destroy (priv->remove_attachment_banner);
660 g_object_unref (priv->remove_attachment_banner);
661 priv->remove_attachment_banner = NULL;
664 if (priv->purge_timeout > 0) {
665 g_source_remove (priv->purge_timeout);
666 priv->purge_timeout = 0;
669 if (priv->row_reference) {
670 gtk_tree_row_reference_free (priv->row_reference);
671 priv->row_reference = NULL;
674 if (priv->next_row_reference) {
675 gtk_tree_row_reference_free (priv->next_row_reference);
676 priv->next_row_reference = NULL;
680 g_free (priv->msg_uid);
681 priv->msg_uid = NULL;
684 G_OBJECT_CLASS(parent_class)->finalize (obj);
688 select_next_valid_row (GtkTreeModel *model,
689 GtkTreeRowReference **row_reference,
692 GtkTreeIter tmp_iter;
694 GtkTreePath *next = NULL;
695 gboolean retval = FALSE;
697 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
699 path = gtk_tree_row_reference_get_path (*row_reference);
700 gtk_tree_model_get_iter (model, &tmp_iter, path);
701 gtk_tree_row_reference_free (*row_reference);
702 *row_reference = NULL;
704 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
705 next = gtk_tree_model_get_path (model, &tmp_iter);
706 *row_reference = gtk_tree_row_reference_new (model, next);
708 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
709 next = gtk_tree_model_get_path (model, &tmp_iter);
711 /* Ensure that we are not selecting the same */
712 if (gtk_tree_path_compare (path, next) != 0) {
713 *row_reference = gtk_tree_row_reference_new (model, next);
719 gtk_tree_path_free (path);
721 gtk_tree_path_free (next);
726 /* TODO: This should be in _init(), with the parameters as properties. */
728 modest_msg_view_window_construct (ModestMsgViewWindow *self,
729 const gchar *modest_account_name,
730 const gchar *msg_uid)
733 ModestMsgViewWindowPrivate *priv = NULL;
734 ModestWindowPrivate *parent_priv = NULL;
735 ModestDimmingRulesGroup *menu_rules_group = NULL;
736 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
737 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
739 obj = G_OBJECT (self);
740 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
741 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
743 priv->msg_uid = g_strdup (msg_uid);
746 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
747 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
748 gtk_widget_show (parent_priv->menubar);
749 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
751 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
752 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
753 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
755 /* Add common dimming rules */
756 modest_dimming_rules_group_add_rules (menu_rules_group,
757 modest_msg_view_menu_dimming_entries,
758 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
759 MODEST_WINDOW (self));
760 modest_dimming_rules_group_add_rules (toolbar_rules_group,
761 modest_msg_view_toolbar_dimming_entries,
762 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
763 MODEST_WINDOW (self));
764 modest_dimming_rules_group_add_rules (clipboard_rules_group,
765 modest_msg_view_clipboard_dimming_entries,
766 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
767 MODEST_WINDOW (self));
769 /* Insert dimming rules group for this window */
770 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
771 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
772 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
773 g_object_unref (menu_rules_group);
774 g_object_unref (toolbar_rules_group);
775 g_object_unref (clipboard_rules_group);
777 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
779 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
781 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);
782 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
783 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
784 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
785 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
786 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
787 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
788 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
789 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
790 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
791 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
793 g_signal_connect (G_OBJECT (obj), "key-release-event",
794 G_CALLBACK (modest_msg_view_window_key_event),
797 g_signal_connect (G_OBJECT (obj), "key-press-event",
798 G_CALLBACK (modest_msg_view_window_key_event),
801 g_signal_connect (G_OBJECT (obj), "window-state-event",
802 G_CALLBACK (modest_msg_view_window_window_state_event),
805 g_signal_connect (G_OBJECT (obj), "move-focus",
806 G_CALLBACK (on_move_focus), obj);
808 /* Mail Operation Queue */
809 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
811 G_CALLBACK (on_queue_changed),
814 /* Account manager */
815 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
817 G_CALLBACK(on_account_removed),
820 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
822 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
823 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
824 priv->last_search = NULL;
826 /* Init the clipboard actions dim status */
827 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
829 gtk_widget_show_all (GTK_WIDGET (obj));
830 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
835 /* FIXME: parameter checks */
837 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
838 const gchar *modest_account_name,
839 const gchar *msg_uid,
841 GtkTreeRowReference *row_reference)
843 ModestMsgViewWindow *window = NULL;
844 ModestMsgViewWindowPrivate *priv = NULL;
845 TnyFolder *header_folder = NULL;
846 ModestHeaderView *header_view = NULL;
847 ModestWindow *main_window = NULL;
848 ModestWindowMgr *mgr = NULL;
851 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
854 mgr = modest_runtime_get_window_mgr ();
855 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
856 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
858 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
860 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
862 /* Remember the message list's TreeModel so we can detect changes
863 * and change the list selection when necessary: */
865 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
867 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
868 MODEST_MAIN_WINDOW(main_window),
869 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
872 if (header_view != NULL){
873 header_folder = modest_header_view_get_folder(header_view);
874 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
875 g_assert(header_folder != NULL);
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);
881 priv->header_model = g_object_ref(model);
883 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
884 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
885 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
887 priv->row_reference = NULL;
888 priv->next_row_reference = NULL;
891 priv->row_changed_handler = g_signal_connect(
892 GTK_TREE_MODEL(model), "row-changed",
893 G_CALLBACK(modest_msg_view_window_on_row_changed),
895 priv->row_deleted_handler = g_signal_connect(
896 GTK_TREE_MODEL(model), "row-deleted",
897 G_CALLBACK(modest_msg_view_window_on_row_deleted),
899 priv->row_inserted_handler = g_signal_connect (
900 GTK_TREE_MODEL(model), "row-inserted",
901 G_CALLBACK(modest_msg_view_window_on_row_inserted),
903 priv->rows_reordered_handler = g_signal_connect(
904 GTK_TREE_MODEL(model), "rows-reordered",
905 G_CALLBACK(modest_msg_view_window_on_row_reordered),
908 if (header_view != NULL){
909 modest_header_view_add_observer(header_view,
910 MODEST_HEADER_VIEW_OBSERVER(window));
913 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
914 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
915 gtk_widget_show_all (GTK_WIDGET (window));
916 modest_msg_view_window_update_priority (window);
918 /* Check dimming rules */
919 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
920 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
921 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
923 return MODEST_WINDOW(window);
927 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
928 const gchar *modest_account_name,
929 const gchar *msg_uid)
931 ModestMsgViewWindow *window = NULL;
932 ModestMsgViewWindowPrivate *priv = NULL;
933 ModestWindowMgr *mgr = NULL;
935 mgr = modest_runtime_get_window_mgr ();
936 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
937 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
938 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
940 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
942 /* Remember that this is a search result,
943 * so we can disable some UI appropriately: */
944 priv->is_search_result = TRUE;
946 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
948 update_window_title (window);
949 modest_msg_view_window_update_priority (window);
952 return MODEST_WINDOW(window);
956 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
957 const gchar *modest_account_name,
958 const gchar *msg_uid)
961 ModestMsgViewWindowPrivate *priv;
962 ModestWindowMgr *mgr = NULL;
964 g_return_val_if_fail (msg, NULL);
965 mgr = modest_runtime_get_window_mgr ();
966 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
967 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
968 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
969 modest_account_name, msg_uid);
971 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
972 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
974 /* Check dimming rules */
975 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
976 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
977 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
979 return MODEST_WINDOW(obj);
982 void modest_msg_view_window_on_row_changed(
983 GtkTreeModel *header_model,
986 ModestMsgViewWindow *window){
987 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
990 void modest_msg_view_window_on_row_deleted(
991 GtkTreeModel *header_model,
993 ModestMsgViewWindow *window){
994 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
997 /* On insertions we check if the folder still has the message we are
998 * showing or do not. If do not, we do nothing. Which means we are still
999 * not attached to any header folder and thus next/prev buttons are
1000 * still dimmed. Once the message that is shown by msg-view is found, the
1001 * new model of header-view will be attached and the references will be set.
1002 * On each further insertions dimming rules will be checked. However
1003 * this requires extra CPU time at least works.
1004 * (An message might be deleted from TnyFolder and thus will not be
1005 * inserted into the model again for example if it is removed by the
1006 * imap server and the header view is refreshed.)
1008 void modest_msg_view_window_on_row_inserted(
1009 GtkTreeModel *new_model,
1010 GtkTreePath *tree_path,
1011 GtkTreeIter *tree_iter,
1012 ModestMsgViewWindow *window){
1013 ModestMsgViewWindowPrivate *priv = NULL;
1014 TnyHeader *header = NULL;
1017 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1019 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1021 /* If we already has a model attached then the message shown by
1022 * msg-view is in it, and thus we do not need any actions but
1023 * to check the dimming rules.*/
1024 if(priv->header_model != NULL){
1025 if (priv->row_reference) {
1026 gtk_tree_row_reference_free(priv->next_row_reference);
1027 priv->next_row_reference = gtk_tree_row_reference_copy(
1028 priv->row_reference);
1029 select_next_valid_row (priv->header_model,
1030 &(priv->next_row_reference), FALSE);
1032 modest_ui_actions_check_toolbar_dimming_rules (
1033 MODEST_WINDOW (window));
1037 /* Check if the newly inserted message is the same we are actually
1038 * showing. IF not, we should remain detached from the header model
1039 * and thus prev and next toolbarbuttons should remain dimmed. */
1040 gtk_tree_model_get (new_model, tree_iter,
1041 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1042 uid = modest_tny_folder_get_header_unique_id(header);
1043 if(!g_str_equal(priv->msg_uid, uid)){
1045 g_object_unref(G_OBJECT(header));
1049 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1050 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1051 g_object_unref(G_OBJECT(header));
1055 /* Setup row_reference for the actual msg. */
1056 priv->row_reference = gtk_tree_row_reference_new(
1057 new_model, tree_path);
1058 if(priv->row_reference == NULL){
1059 g_warning("No reference for msg header item.");
1063 /* Attach new_model and connect some callback to it to become able
1064 * to detect changes in header-view. */
1065 priv->header_model = g_object_ref(new_model);
1066 g_signal_connect (new_model, "row-changed",
1067 G_CALLBACK (modest_msg_view_window_on_row_changed),
1069 g_signal_connect (new_model, "row-deleted",
1070 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1072 g_signal_connect (new_model, "rows-reordered",
1073 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1076 /* Now set up next_row_reference. */
1077 priv->next_row_reference = gtk_tree_row_reference_copy(
1078 priv->row_reference);
1079 select_next_valid_row (priv->header_model,
1080 &(priv->next_row_reference), FALSE);
1082 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1085 void modest_msg_view_window_on_row_reordered(
1086 GtkTreeModel *header_model,
1090 ModestMsgViewWindow *window){
1091 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1094 /* The modest_msg_view_window_update_model_replaced implements update
1095 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1096 * actually belongs to the header-view is the same as the TnyFolder of
1097 * the message of msg-view or not. If they are different, there is
1098 * nothing to do. If they are the same, then the model has replaced and
1099 * the reference in msg-view shall be replaced from the old model to
1100 * the new model. In this case the view will be detached from it's
1101 * header folder. From this point the next/prev buttons are dimmed.
1103 void modest_msg_view_window_update_model_replaced(
1104 ModestHeaderViewObserver *observer,
1105 GtkTreeModel *model,
1106 const gchar *tny_folder_id){
1107 ModestMsgViewWindowPrivate *priv = NULL;
1108 ModestMsgViewWindow *window = NULL;
1110 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1111 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1113 window = MODEST_MSG_VIEW_WINDOW(observer);
1114 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1116 /* If there is an other folder in the header-view then we do
1117 * not care about it's model (msg list). Else if the
1118 * header-view shows the folder the msg shown by us is in, we
1119 * shall replace our model reference and make some check. */
1120 if(tny_folder_id == NULL || !g_str_equal(tny_folder_id, priv->header_folder_id))
1123 /* Model is changed(replaced), so we should forget the old
1124 * one. Because there might be other references and there
1125 * might be some change on the model even if we unreferenced
1126 * it, we need to disconnect our signals here. */
1127 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1128 priv->row_changed_handler))
1129 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1130 priv->row_changed_handler);
1131 priv->row_changed_handler = 0;
1132 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1133 priv->row_deleted_handler))
1134 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1135 priv->row_deleted_handler);
1136 priv->row_deleted_handler = 0;
1137 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1138 priv->row_inserted_handler))
1139 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1140 priv->row_inserted_handler);
1141 priv->row_inserted_handler = 0;
1142 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1143 priv->rows_reordered_handler))
1144 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1145 priv->rows_reordered_handler);
1146 priv->rows_reordered_handler = 0;
1147 g_object_unref(priv->header_model);
1148 priv->header_model = NULL;
1149 if (priv->row_reference)
1150 gtk_tree_row_reference_free (priv->row_reference);
1151 priv->row_reference = NULL;
1152 if (priv->next_row_reference)
1153 gtk_tree_row_reference_free (priv->next_row_reference);
1154 priv->next_row_reference = NULL;
1156 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1158 g_assert(model != NULL);
1160 /* Also we must connect to the new model for row insertions.
1161 * Only for insertions now. We will need other ones only after
1162 * the msg is show by msg-view is added to the new model. */
1163 priv->row_inserted_handler = g_signal_connect (
1164 model, "row-inserted",
1165 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1170 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1172 ModestMsgViewWindowPrivate *priv= NULL;
1174 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1175 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1177 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1181 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1183 ModestMsgViewWindowPrivate *priv= NULL;
1185 TnyHeader *header = NULL;
1186 GtkTreePath *path = NULL;
1189 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1190 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1192 /* If the message was not obtained from a treemodel,
1193 * for instance if it was opened directly by the search UI:
1195 if (priv->header_model == NULL ||
1196 priv->row_reference == NULL ||
1197 !gtk_tree_row_reference_valid (priv->row_reference)) {
1198 msg = modest_msg_view_window_get_message (self);
1200 header = tny_msg_get_header (msg);
1201 g_object_unref (msg);
1206 /* Get iter of the currently selected message in the header view: */
1207 path = gtk_tree_row_reference_get_path (priv->row_reference);
1208 g_return_val_if_fail (path != NULL, NULL);
1209 gtk_tree_model_get_iter (priv->header_model,
1213 /* Get current message header */
1214 gtk_tree_model_get (priv->header_model, &iter,
1215 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1218 gtk_tree_path_free (path);
1223 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1225 ModestMsgViewWindowPrivate *priv;
1227 g_return_val_if_fail (self, NULL);
1229 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1231 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1235 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1237 ModestMsgViewWindowPrivate *priv;
1239 g_return_val_if_fail (self, NULL);
1241 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1243 return (const gchar*) priv->msg_uid;
1247 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1250 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1251 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1252 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1256 is_active = gtk_toggle_action_get_active (toggle);
1259 gtk_widget_show (priv->find_toolbar);
1260 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1262 gtk_widget_hide (priv->find_toolbar);
1265 /* update the toggle buttons status */
1266 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1267 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1268 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1269 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1274 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1275 ModestMsgViewWindow *obj)
1277 GtkToggleAction *toggle;
1278 ModestWindowPrivate *parent_priv;
1279 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1281 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1282 gtk_toggle_action_set_active (toggle, FALSE);
1286 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1287 ModestMsgViewWindow *obj)
1289 gchar *current_search;
1290 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1292 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1293 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1297 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1299 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1300 g_free (current_search);
1301 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1305 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1307 g_free (priv->last_search);
1308 priv->last_search = g_strdup (current_search);
1309 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1312 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1313 g_free (priv->last_search);
1314 priv->last_search = NULL;
1316 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1319 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1320 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1321 g_free (priv->last_search);
1322 priv->last_search = NULL;
1324 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1328 g_free (current_search);
1333 modest_msg_view_window_set_zoom (ModestWindow *window,
1336 ModestMsgViewWindowPrivate *priv;
1337 ModestWindowPrivate *parent_priv;
1338 GtkAction *action = NULL;
1339 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1341 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1343 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1344 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1345 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1347 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1348 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1350 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1354 modest_msg_view_window_get_zoom (ModestWindow *window)
1356 ModestMsgViewWindowPrivate *priv;
1358 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1360 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1361 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1365 modest_msg_view_window_zoom_plus (ModestWindow *window)
1367 ModestWindowPrivate *parent_priv;
1368 GtkRadioAction *zoom_radio_action;
1369 GSList *group, *node;
1371 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1372 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1373 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1375 group = gtk_radio_action_get_group (zoom_radio_action);
1377 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1378 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1382 for (node = group; node != NULL; node = g_slist_next (node)) {
1383 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1384 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1392 modest_msg_view_window_zoom_minus (ModestWindow *window)
1394 ModestWindowPrivate *parent_priv;
1395 GtkRadioAction *zoom_radio_action;
1396 GSList *group, *node;
1398 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1399 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1400 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1402 group = gtk_radio_action_get_group (zoom_radio_action);
1404 for (node = group; node != NULL; node = g_slist_next (node)) {
1405 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1406 if (node->next != NULL) {
1407 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1410 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1420 modest_msg_view_window_key_event (GtkWidget *window,
1426 focus = gtk_window_get_focus (GTK_WINDOW (window));
1428 /* for the find toolbar case */
1429 if (focus && GTK_IS_ENTRY (focus)) {
1430 if (event->keyval == GDK_BackSpace) {
1432 copy = gdk_event_copy ((GdkEvent *) event);
1433 gtk_widget_event (focus, copy);
1434 gdk_event_free (copy);
1439 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1440 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1441 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1442 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1443 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1444 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1445 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1446 gboolean return_value;
1448 if (event->type == GDK_KEY_RELEASE) {
1449 GtkScrollType scroll_type;
1451 switch (event->keyval) {
1454 scroll_type = GTK_SCROLL_STEP_UP; break;
1457 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1459 case GDK_KP_Page_Up:
1460 scroll_type = GTK_SCROLL_PAGE_UP; break;
1462 case GDK_KP_Page_Down:
1463 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1466 scroll_type = GTK_SCROLL_START; break;
1469 scroll_type = GTK_SCROLL_END; break;
1470 default: scroll_type = GTK_SCROLL_NONE;
1473 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",
1474 scroll_type, FALSE, &return_value);
1485 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1488 ModestMsgViewWindowPrivate *priv;
1489 GtkTreeIter tmp_iter;
1490 gboolean is_last_selected;
1492 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1493 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1495 /*if no model (so no rows at all), then virtually we are the last*/
1496 if (!priv->header_model || !priv->row_reference)
1499 path = gtk_tree_row_reference_get_path (priv->row_reference);
1503 is_last_selected = TRUE;
1504 while (is_last_selected) {
1506 gtk_tree_path_next (path);
1507 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1509 gtk_tree_model_get (priv->header_model, &tmp_iter,
1510 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1513 if (msg_is_visible (header, priv->is_outbox))
1514 is_last_selected = FALSE;
1515 g_object_unref(G_OBJECT(header));
1518 gtk_tree_path_free (path);
1519 return is_last_selected;
1523 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1525 ModestMsgViewWindowPrivate *priv;
1527 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1528 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1530 return priv->header_model != NULL;
1534 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1536 ModestMsgViewWindowPrivate *priv;
1538 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1539 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1541 return priv->is_search_result;
1545 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1547 return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1548 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1553 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1556 ModestMsgViewWindowPrivate *priv;
1557 gboolean is_first_selected;
1558 GtkTreeIter tmp_iter;
1559 /* gchar * path_string;*/
1561 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1562 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1564 /*if no model (so no rows at all), then virtually we are the first*/
1565 if (!priv->header_model || !priv->row_reference)
1568 path = gtk_tree_row_reference_get_path (priv->row_reference);
1572 /* path_string = gtk_tree_path_to_string (path);
1573 is_first_selected = strcmp (path_string, "0");
1575 g_free (path_string);
1576 gtk_tree_path_free (path);
1578 return is_first_selected;*/
1580 is_first_selected = TRUE;
1581 while (is_first_selected) {
1583 if(!gtk_tree_path_prev (path))
1585 /* Here the 'if' is needless for logic, but let make sure
1586 * iter is valid for gtk_tree_model_get. */
1587 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1589 gtk_tree_model_get (priv->header_model, &tmp_iter,
1590 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1593 if (msg_is_visible (header, priv->is_outbox))
1594 is_first_selected = FALSE;
1595 g_object_unref(G_OBJECT(header));
1598 gtk_tree_path_free (path);
1599 return is_first_selected;
1604 GtkTreeRowReference *row_reference;
1608 message_reader_performer (gboolean canceled,
1610 GtkWindow *parent_window,
1611 TnyAccount *account,
1614 ModestMailOperation *mail_op = NULL;
1615 MsgReaderInfo *info;
1617 info = (MsgReaderInfo *) user_data;
1618 if (canceled || err) {
1622 /* New mail operation */
1623 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1624 modest_ui_actions_get_msgs_full_error_handler,
1627 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1628 modest_mail_operation_get_msg (mail_op, info->header, view_msg_cb, info->row_reference);
1629 g_object_unref (mail_op);
1632 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1633 g_object_unref (info->header);
1634 g_slice_free (MsgReaderInfo, info);
1639 * Reads the message whose summary item is @header. It takes care of
1640 * several things, among others:
1642 * If the message was not previously downloaded then ask the user
1643 * before downloading. If there is no connection launch the connection
1644 * dialog. Update toolbar dimming rules.
1646 * Returns: TRUE if the mail operation was started, otherwise if the
1647 * user do not want to download the message, or if the user do not
1648 * want to connect, then the operation is not issued
1651 message_reader (ModestMsgViewWindow *window,
1652 ModestMsgViewWindowPrivate *priv,
1654 GtkTreeRowReference *row_reference)
1656 gboolean already_showing = FALSE;
1657 ModestWindow *msg_window = NULL;
1658 ModestWindowMgr *mgr;
1659 TnyAccount *account;
1661 MsgReaderInfo *info;
1663 g_return_val_if_fail (row_reference != NULL, FALSE);
1665 mgr = modest_runtime_get_window_mgr ();
1666 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1667 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1670 gtk_window_present (GTK_WINDOW (msg_window));
1671 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1675 /* Msg download completed */
1676 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1677 /* Ask the user if he wants to download the message if
1679 if (!tny_device_is_online (modest_runtime_get_device())) {
1680 GtkResponseType response;
1682 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1683 _("mcen_nc_get_msg"));
1684 if (response == GTK_RESPONSE_CANCEL)
1687 folder = tny_header_get_folder (header);
1688 info = g_slice_new (MsgReaderInfo);
1689 info->header = g_object_ref (header);
1690 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1692 /* Offer the connection dialog if necessary */
1693 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1695 TNY_FOLDER_STORE (folder),
1696 message_reader_performer,
1698 g_object_unref (folder);
1703 folder = tny_header_get_folder (header);
1704 account = tny_folder_get_account (folder);
1705 info = g_slice_new (MsgReaderInfo);
1706 info->header = g_object_ref (header);
1707 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1709 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1710 g_object_unref (account);
1711 g_object_unref (folder);
1717 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1719 ModestMsgViewWindowPrivate *priv;
1720 GtkTreePath *path= NULL;
1721 GtkTreeIter tmp_iter;
1723 gboolean retval = TRUE;
1724 GtkTreeRowReference *row_reference = NULL;
1726 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1727 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1729 if (!priv->row_reference)
1732 /* Update the next row reference if it's not valid. This could
1733 happen if for example the header which it was pointing to,
1734 was deleted. The best place to do it is in the row-deleted
1735 handler but the tinymail model do not work like the glib
1736 tree models and reports the deletion when the row is still
1738 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1739 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1740 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1741 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1744 if (priv->next_row_reference)
1745 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1749 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1751 gtk_tree_model_get_iter (priv->header_model,
1754 gtk_tree_path_free (path);
1756 gtk_tree_model_get (priv->header_model, &tmp_iter,
1757 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1760 /* Read the message & show it */
1761 if (!message_reader (window, priv, header, row_reference)) {
1764 gtk_tree_row_reference_free (row_reference);
1767 g_object_unref (header);
1773 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1775 ModestMsgViewWindowPrivate *priv = NULL;
1777 gboolean finished = FALSE;
1778 gboolean retval = FALSE;
1780 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1781 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1783 /* Return inmediatly if there is no header model */
1784 if (!priv->header_model || !priv->row_reference)
1787 path = gtk_tree_row_reference_get_path (priv->row_reference);
1788 while (!finished && gtk_tree_path_prev (path)) {
1792 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1793 gtk_tree_model_get (priv->header_model, &iter,
1794 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1798 if (msg_is_visible (header, priv->is_outbox)) {
1799 GtkTreeRowReference *row_reference;
1800 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1801 /* Read the message & show it */
1802 retval = message_reader (window, priv, header, row_reference);
1803 gtk_tree_row_reference_free (row_reference);
1807 g_object_unref (header);
1811 gtk_tree_path_free (path);
1816 view_msg_cb (ModestMailOperation *mail_op,
1823 ModestMsgViewWindow *self = NULL;
1824 ModestMsgViewWindowPrivate *priv = NULL;
1825 GtkTreeRowReference *row_reference = NULL;
1827 row_reference = (GtkTreeRowReference *) user_data;
1829 gtk_tree_row_reference_free (row_reference);
1833 /* If there was any error */
1834 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1835 gtk_tree_row_reference_free (row_reference);
1839 /* Get the window */
1840 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1841 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1842 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1844 /* Update the row reference */
1845 if (priv->row_reference != NULL) {
1846 gtk_tree_row_reference_free (priv->row_reference);
1847 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1848 if (priv->next_row_reference != NULL) {
1849 gtk_tree_row_reference_free (priv->next_row_reference);
1851 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1852 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1855 /* Mark header as read */
1856 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1857 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1859 /* Set new message */
1860 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1861 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1862 modest_msg_view_window_update_priority (self);
1863 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1864 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1867 /* Set the new message uid of the window */
1868 if (priv->msg_uid) {
1869 g_free (priv->msg_uid);
1870 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1873 /* Notify the observers */
1874 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1875 0, priv->header_model, priv->row_reference);
1878 g_object_unref (self);
1879 gtk_tree_row_reference_free (row_reference);
1883 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1885 ModestMsgViewWindowPrivate *priv;
1887 TnyFolderType folder_type;
1889 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1891 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1893 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1897 folder = tny_msg_get_folder (msg);
1899 folder_type = modest_tny_folder_guess_folder_type (folder);
1900 g_object_unref (folder);
1902 g_object_unref (msg);
1910 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1912 ModestMsgViewWindowPrivate *priv;
1913 TnyHeader *header = NULL;
1914 TnyHeaderFlags flags = 0;
1916 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1918 if (priv->header_model && priv->row_reference) {
1920 GtkTreePath *path = NULL;
1922 path = gtk_tree_row_reference_get_path (priv->row_reference);
1923 g_return_if_fail (path != NULL);
1924 gtk_tree_model_get_iter (priv->header_model,
1926 gtk_tree_row_reference_get_path (priv->row_reference));
1928 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1930 gtk_tree_path_free (path);
1933 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1935 header = tny_msg_get_header (msg);
1936 g_object_unref (msg);
1941 flags = tny_header_get_flags (header);
1942 g_object_unref(G_OBJECT(header));
1945 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1950 toolbar_resize (ModestMsgViewWindow *self)
1952 ModestMsgViewWindowPrivate *priv = NULL;
1953 ModestWindowPrivate *parent_priv = NULL;
1955 gint static_button_size;
1956 ModestWindowMgr *mgr;
1958 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1959 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1960 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1962 mgr = modest_runtime_get_window_mgr ();
1963 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
1965 if (parent_priv->toolbar) {
1966 /* left size buttons */
1967 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
1968 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1969 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1970 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1971 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
1972 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1973 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1974 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1975 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
1976 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1977 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1978 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1979 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1980 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1981 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1982 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1984 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
1985 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1986 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1987 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1988 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1989 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1990 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1991 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1997 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1999 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2000 ModestWindowPrivate *parent_priv;
2001 ModestWindowMgr *mgr;
2002 gboolean is_fullscreen;
2003 GtkAction *fs_toggle_action;
2006 mgr = modest_runtime_get_window_mgr ();
2007 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2009 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2011 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2012 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2013 if (is_fullscreen != active) {
2014 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2016 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2024 modest_msg_view_window_show_toolbar (ModestWindow *self,
2025 gboolean show_toolbar)
2027 ModestMsgViewWindowPrivate *priv = NULL;
2028 ModestWindowPrivate *parent_priv;
2029 GtkWidget *reply_button = NULL, *menu = NULL;
2030 GtkWidget *placeholder = NULL;
2032 const gchar *action_name;
2035 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2036 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2038 /* Set optimized view status */
2039 priv->optimized_view = !show_toolbar;
2041 if (!parent_priv->toolbar) {
2042 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2044 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2046 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2047 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2048 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2049 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2050 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2052 /* Add ProgressBar (Transfer toolbar) */
2053 priv->progress_bar = modest_progress_bar_new ();
2054 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2055 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2056 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2057 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2058 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2060 /* Connect cancel 'clicked' signal to abort progress mode */
2061 g_signal_connect(priv->cancel_toolitem, "clicked",
2062 G_CALLBACK(cancel_progressbar),
2065 /* Add it to the observers list */
2066 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2069 hildon_window_add_toolbar (HILDON_WINDOW (self),
2070 GTK_TOOLBAR (parent_priv->toolbar));
2072 /* Set reply button tap and hold menu */
2073 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2074 "/ToolBar/ToolbarMessageReply");
2075 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2076 "/ToolbarReplyCSM");
2077 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2081 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2082 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2083 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2085 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2086 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2087 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2089 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2092 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2093 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2096 /* Update also the actions (to update the toggles in the
2097 menus), we have to do it manually because some other window
2098 of the same time could have changed it (remember that the
2099 toolbar fullscreen mode is shared by all the windows of the
2101 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2102 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2104 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2106 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2107 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2112 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2114 ModestMsgViewWindow *window)
2116 if (!GTK_WIDGET_VISIBLE (window))
2119 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2123 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2125 ModestMsgViewWindowPrivate *priv;
2127 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2128 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2130 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2134 cancel_progressbar (GtkToolButton *toolbutton,
2135 ModestMsgViewWindow *self)
2138 ModestMsgViewWindowPrivate *priv;
2140 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2142 /* Get operation observers and cancel its current operation */
2143 tmp = priv->progress_widgets;
2145 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2146 tmp=g_slist_next(tmp);
2150 observers_empty (ModestMsgViewWindow *self)
2153 ModestMsgViewWindowPrivate *priv;
2154 gboolean is_empty = TRUE;
2155 guint pending_ops = 0;
2157 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2158 tmp = priv->progress_widgets;
2160 /* Check all observers */
2161 while (tmp && is_empty) {
2162 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2163 is_empty = pending_ops == 0;
2165 tmp = g_slist_next(tmp);
2172 on_account_removed (TnyAccountStore *account_store,
2173 TnyAccount *account,
2176 /* Do nothing if it's a transport account, because we only
2177 show the messages of a store account */
2178 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2179 const gchar *parent_acc = NULL;
2180 const gchar *our_acc = NULL;
2182 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2183 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2185 /* Close this window if I'm showing a message of the removed account */
2186 if (strcmp (parent_acc, our_acc) == 0)
2187 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2192 on_mail_operation_started (ModestMailOperation *mail_op,
2195 ModestMsgViewWindow *self;
2196 ModestMailOperationTypeOperation op_type;
2198 ModestMsgViewWindowPrivate *priv;
2199 GObject *source = NULL;
2201 self = MODEST_MSG_VIEW_WINDOW (user_data);
2202 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2203 op_type = modest_mail_operation_get_type_operation (mail_op);
2204 tmp = priv->progress_widgets;
2205 source = modest_mail_operation_get_source(mail_op);
2206 if (G_OBJECT (self) == source) {
2207 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2208 set_toolbar_transfer_mode(self);
2210 modest_progress_object_add_operation (
2211 MODEST_PROGRESS_OBJECT (tmp->data),
2213 tmp = g_slist_next (tmp);
2217 g_object_unref (source);
2221 on_mail_operation_finished (ModestMailOperation *mail_op,
2224 ModestMsgViewWindow *self;
2225 ModestMailOperationTypeOperation op_type;
2227 ModestMsgViewWindowPrivate *priv;
2229 self = MODEST_MSG_VIEW_WINDOW (user_data);
2230 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2231 op_type = modest_mail_operation_get_type_operation (mail_op);
2232 tmp = priv->progress_widgets;
2234 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2236 modest_progress_object_remove_operation (
2237 MODEST_PROGRESS_OBJECT (tmp->data),
2239 tmp = g_slist_next (tmp);
2242 /* If no more operations are being observed, NORMAL mode is enabled again */
2243 if (observers_empty (self)) {
2244 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2247 /* Update dimming rules. We have to do this right here
2248 and not in view_msg_cb because at that point the
2249 transfer mode is still enabled so the dimming rule
2250 won't let the user delete the message that has been
2251 readed for example */
2252 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2253 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2258 on_queue_changed (ModestMailOperationQueue *queue,
2259 ModestMailOperation *mail_op,
2260 ModestMailOperationQueueNotification type,
2261 ModestMsgViewWindow *self)
2263 ModestMsgViewWindowPrivate *priv;
2265 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2267 /* If this operations was created by another window, do nothing */
2268 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2271 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2272 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2274 "operation-started",
2275 G_CALLBACK (on_mail_operation_started),
2277 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2279 "operation-finished",
2280 G_CALLBACK (on_mail_operation_finished),
2282 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2283 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2285 "operation-started");
2286 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2288 "operation-finished");
2293 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2295 ModestMsgViewWindowPrivate *priv;
2296 TnyList *selected_attachments = NULL;
2298 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2299 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2301 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2303 return selected_attachments;
2307 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2309 ModestMsgViewWindowPrivate *priv;
2310 const gchar *msg_uid;
2311 gchar *attachment_uid = NULL;
2312 gint attachment_index = 0;
2313 TnyList *attachments;
2315 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2316 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2317 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2319 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2320 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2321 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2322 g_object_unref (attachments);
2324 if (msg_uid && attachment_index >= 0) {
2325 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2328 if (mime_part == NULL) {
2329 gboolean error = FALSE;
2330 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2331 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2333 } else if (tny_list_get_length (selected_attachments) > 1) {
2334 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2338 iter = tny_list_create_iterator (selected_attachments);
2339 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2340 g_object_unref (iter);
2342 g_object_unref (selected_attachments);
2347 g_object_ref (mime_part);
2350 if (tny_mime_part_is_purged (mime_part)) {
2351 g_object_unref (mime_part);
2355 if (!modest_tny_mime_part_is_msg (mime_part)) {
2356 gchar *filepath = NULL;
2357 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2358 const gchar *content_type;
2359 gboolean show_error_banner = FALSE;
2361 TnyFsStream *temp_stream = NULL;
2362 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2365 if (temp_stream != NULL) {
2366 content_type = tny_mime_part_get_content_type (mime_part);
2367 if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2368 /* make the file read-only */
2369 if (g_chmod(filepath, 0444) != 0)
2370 g_warning ("%s: failed to set file '%s' to read-only: %s",
2371 __FUNCTION__, filepath, strerror(errno));
2373 modest_platform_activate_file (filepath, content_type);
2375 /* error while saving attachment, maybe cerm_device_memory_full */
2376 show_error_banner = TRUE;
2378 g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2382 g_object_unref (temp_stream);
2384 /* NOTE: files in the temporary area will be automatically
2385 * cleaned after some time if they are no longer in use */
2387 if (filepath != NULL) {
2388 /* the file may already exist but it isn't writable,
2389 * let's try to open it anyway */
2390 content_type = tny_mime_part_get_content_type (mime_part);
2391 modest_platform_activate_file (filepath, content_type);
2394 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2395 show_error_banner = TRUE;
2398 if (show_error_banner)
2399 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2401 /* message attachment */
2402 TnyHeader *header = NULL;
2403 ModestWindowMgr *mgr;
2404 ModestWindow *msg_win = NULL;
2407 header = tny_msg_get_header (TNY_MSG (mime_part));
2408 mgr = modest_runtime_get_window_mgr ();
2409 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2412 if (msg_win) /* there is already a window for this uid; top it */
2413 gtk_window_present (GTK_WINDOW(msg_win));
2415 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2416 * thus, we don't do anything */
2417 g_warning ("window for is already being created");
2419 /* it's not found, so create a new window for it */
2420 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2421 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2423 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2424 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2425 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2426 modest_window_get_zoom (MODEST_WINDOW (window)));
2427 modest_window_mgr_register_window (mgr, msg_win);
2428 gtk_widget_show_all (GTK_WIDGET (msg_win));
2431 g_object_unref (mime_part);
2444 GnomeVFSResult result;
2447 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2448 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2449 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2450 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2453 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2457 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2458 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2459 g_free (pair->filename);
2460 g_object_unref (pair->part);
2461 g_slice_free (SaveMimePartPair, pair);
2463 g_list_free (info->pairs);
2466 gtk_widget_destroy (info->banner);
2467 g_slice_free (SaveMimePartInfo, info);
2472 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2474 if (info->pairs != NULL) {
2475 save_mime_part_to_file (info);
2477 /* This is a GDK lock because we are an idle callback and
2478 * hildon_banner_show_information is or does Gtk+ code */
2480 gdk_threads_enter (); /* CHECKED */
2481 save_mime_part_info_free (info, TRUE);
2482 if (info->result == GNOME_VFS_OK) {
2483 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2484 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2485 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2486 "cerm_device_memory_full"));
2488 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2490 gdk_threads_leave (); /* CHECKED */
2497 save_mime_part_to_file (SaveMimePartInfo *info)
2499 GnomeVFSHandle *handle;
2501 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2503 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2504 if (info->result == GNOME_VFS_OK) {
2505 stream = tny_vfs_stream_new (handle);
2506 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2507 info->result = GNOME_VFS_ERROR_IO;
2509 g_object_unref (G_OBJECT (stream));
2510 g_object_unref (pair->part);
2511 g_slice_free (SaveMimePartPair, pair);
2512 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2514 save_mime_part_info_free (info, FALSE);
2517 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2522 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2524 gboolean is_ok = TRUE;
2525 gint replaced_files = 0;
2526 const GList *files = info->pairs;
2529 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2530 SaveMimePartPair *pair = iter->data;
2531 if (modest_utils_file_exists (pair->filename)) {
2535 if (replaced_files) {
2536 GtkWidget *confirm_overwrite_dialog;
2537 const gchar *message = (replaced_files == 1) ?
2538 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2539 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2540 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2543 gtk_widget_destroy (confirm_overwrite_dialog);
2547 save_mime_part_info_free (info, TRUE);
2549 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2550 _CS("sfil_ib_saving"));
2551 info->banner = banner;
2552 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2559 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2561 ModestMsgViewWindowPrivate *priv;
2562 GList *files_to_save = NULL;
2563 GtkWidget *save_dialog = NULL;
2564 gchar *folder = NULL;
2565 gboolean canceled = FALSE;
2566 const gchar *filename = NULL;
2567 gchar *save_multiple_str = NULL;
2569 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2570 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2572 if (mime_parts == NULL) {
2573 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2574 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2577 g_object_ref (mime_parts);
2580 /* prepare dialog */
2581 if (tny_list_get_length (mime_parts) == 1) {
2583 /* only one attachment selected */
2584 iter = tny_list_create_iterator (mime_parts);
2585 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2586 g_object_unref (iter);
2587 if (!modest_tny_mime_part_is_msg (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2588 filename = tny_mime_part_get_filename (mime_part);
2590 g_warning ("Tried to save a non-file attachment");
2593 g_object_unref (mime_part);
2595 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2596 tny_list_get_length (mime_parts));
2599 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2600 GTK_FILE_CHOOSER_ACTION_SAVE);
2603 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2604 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2608 if (filename != NULL)
2609 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2612 /* if multiple, set multiple string */
2613 if (save_multiple_str) {
2614 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2618 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2619 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2621 if (!modest_utils_folder_writable (chooser_uri)) {
2622 hildon_banner_show_information
2623 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2627 iter = tny_list_create_iterator (mime_parts);
2628 while (!tny_iterator_is_done (iter)) {
2629 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2631 if ((tny_mime_part_is_attachment (mime_part)) &&
2632 (tny_mime_part_get_filename (mime_part) != NULL)) {
2633 SaveMimePartPair *pair;
2635 pair = g_slice_new0 (SaveMimePartPair);
2636 if (save_multiple_str) {
2638 g_build_filename (chooser_uri,
2639 tny_mime_part_get_filename (mime_part), NULL);
2641 pair->filename = g_strdup (chooser_uri);
2643 pair->part = mime_part;
2644 files_to_save = g_list_prepend (files_to_save, pair);
2646 tny_iterator_next (iter);
2648 g_object_unref (iter);
2650 g_free (chooser_uri);
2653 gtk_widget_destroy (save_dialog);
2655 g_object_unref (mime_parts);
2657 if (files_to_save != NULL) {
2658 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2659 info->pairs = files_to_save;
2660 info->result = TRUE;
2661 save_mime_parts_to_file_with_checks (info);
2666 show_remove_attachment_information (gpointer userdata)
2668 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2669 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2671 /* We're outside the main lock */
2672 gdk_threads_enter ();
2674 if (priv->remove_attachment_banner != NULL) {
2675 gtk_widget_destroy (priv->remove_attachment_banner);
2676 g_object_unref (priv->remove_attachment_banner);
2679 priv->remove_attachment_banner = g_object_ref (
2680 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2682 gdk_threads_leave ();
2688 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2690 ModestMsgViewWindowPrivate *priv;
2691 TnyList *mime_parts = NULL;
2692 gchar *confirmation_message;
2697 /* TnyFolder *folder; */
2699 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2700 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2703 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2705 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2707 /* Remove already purged messages from mime parts list */
2708 iter = tny_list_create_iterator (mime_parts);
2709 while (!tny_iterator_is_done (iter)) {
2710 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2711 tny_iterator_next (iter);
2712 if (tny_mime_part_is_purged (part)) {
2713 tny_list_remove (mime_parts, (GObject *) part);
2715 g_object_unref (part);
2717 g_object_unref (iter);
2719 if (tny_list_get_length (mime_parts) == 0) {
2720 g_object_unref (mime_parts);
2724 n_attachments = tny_list_get_length (mime_parts);
2725 if (n_attachments == 1) {
2726 const gchar *filename;
2729 iter = tny_list_create_iterator (mime_parts);
2730 part = (TnyMimePart *) tny_iterator_get_current (iter);
2731 g_object_unref (iter);
2732 if (modest_tny_mime_part_is_msg (part)) {
2734 header = tny_msg_get_header (TNY_MSG (part));
2735 filename = tny_header_get_subject (header);
2736 g_object_unref (header);
2737 if (filename == NULL)
2738 filename = _("mail_va_no_subject");
2740 filename = tny_mime_part_get_filename (TNY_MIME_PART (part));
2742 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2743 g_object_unref (part);
2745 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2746 "mcen_nc_purge_files_text",
2747 n_attachments), n_attachments);
2749 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2750 confirmation_message);
2751 g_free (confirmation_message);
2753 if (response != GTK_RESPONSE_OK) {
2754 g_object_unref (mime_parts);
2758 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2759 /* folder = tny_msg_get_folder (msg); */
2760 /* tny_msg_uncache_attachments (msg); */
2761 /* tny_folder_refresh (folder, NULL); */
2762 /* g_object_unref (folder); */
2764 iter = tny_list_create_iterator (mime_parts);
2765 while (!tny_iterator_is_done (iter)) {
2768 part = (TnyMimePart *) tny_iterator_get_current (iter);
2769 tny_mime_part_set_purged (TNY_MIME_PART (part));
2770 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2771 g_object_unref (part);
2772 tny_iterator_next (iter);
2774 g_object_unref (iter);
2776 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2777 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2778 tny_msg_rewrite_cache (msg);
2779 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2780 g_object_unref (msg);
2782 g_object_unref (mime_parts);
2784 if (priv->purge_timeout > 0) {
2785 g_source_remove (priv->purge_timeout);
2786 priv->purge_timeout = 0;
2789 if (priv->remove_attachment_banner) {
2790 gtk_widget_destroy (priv->remove_attachment_banner);
2791 g_object_unref (priv->remove_attachment_banner);
2792 priv->remove_attachment_banner = NULL;
2800 update_window_title (ModestMsgViewWindow *window)
2802 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2804 TnyHeader *header = NULL;
2805 const gchar *subject = NULL;
2807 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2810 header = tny_msg_get_header (msg);
2811 subject = tny_header_get_subject (header);
2812 g_object_unref (msg);
2815 if ((subject == NULL)||(subject[0] == '\0'))
2816 subject = _("mail_va_no_subject");
2818 gtk_window_set_title (GTK_WINDOW (window), subject);
2822 static void on_move_focus (GtkWidget *widget,
2823 GtkDirectionType direction,
2826 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");