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);
1631 /* Update dimming rules */
1632 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1633 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1636 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1637 g_object_unref (info->header);
1638 g_slice_free (MsgReaderInfo, info);
1643 * Reads the message whose summary item is @header. It takes care of
1644 * several things, among others:
1646 * If the message was not previously downloaded then ask the user
1647 * before downloading. If there is no connection launch the connection
1648 * dialog. Update toolbar dimming rules.
1650 * Returns: TRUE if the mail operation was started, otherwise if the
1651 * user do not want to download the message, or if the user do not
1652 * want to connect, then the operation is not issued
1655 message_reader (ModestMsgViewWindow *window,
1656 ModestMsgViewWindowPrivate *priv,
1658 GtkTreeRowReference *row_reference)
1660 gboolean already_showing = FALSE;
1661 ModestWindow *msg_window = NULL;
1662 ModestWindowMgr *mgr;
1663 TnyAccount *account;
1665 MsgReaderInfo *info;
1667 g_return_val_if_fail (row_reference != NULL, FALSE);
1669 mgr = modest_runtime_get_window_mgr ();
1670 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1671 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1674 gtk_window_present (GTK_WINDOW (msg_window));
1675 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1679 /* Msg download completed */
1680 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1681 /* Ask the user if he wants to download the message if
1683 if (!tny_device_is_online (modest_runtime_get_device())) {
1684 GtkResponseType response;
1686 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1687 _("mcen_nc_get_msg"));
1688 if (response == GTK_RESPONSE_CANCEL)
1691 folder = tny_header_get_folder (header);
1692 info = g_slice_new (MsgReaderInfo);
1693 info->header = g_object_ref (header);
1694 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1696 /* Offer the connection dialog if necessary */
1697 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1699 TNY_FOLDER_STORE (folder),
1700 message_reader_performer,
1702 g_object_unref (folder);
1707 folder = tny_header_get_folder (header);
1708 account = tny_folder_get_account (folder);
1709 info = g_slice_new (MsgReaderInfo);
1710 info->header = g_object_ref (header);
1711 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1713 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1714 g_object_unref (account);
1715 g_object_unref (folder);
1721 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1723 ModestMsgViewWindowPrivate *priv;
1724 GtkTreePath *path= NULL;
1725 GtkTreeIter tmp_iter;
1727 gboolean retval = TRUE;
1728 GtkTreeRowReference *row_reference = NULL;
1730 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1731 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1733 if (!priv->row_reference)
1736 /* Update the next row reference if it's not valid. This could
1737 happen if for example the header which it was pointing to,
1738 was deleted. The best place to do it is in the row-deleted
1739 handler but the tinymail model do not work like the glib
1740 tree models and reports the deletion when the row is still
1742 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1743 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1744 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1745 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1748 if (priv->next_row_reference)
1749 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1753 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1755 gtk_tree_model_get_iter (priv->header_model,
1758 gtk_tree_path_free (path);
1760 gtk_tree_model_get (priv->header_model, &tmp_iter,
1761 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1764 /* Read the message & show it */
1765 if (!message_reader (window, priv, header, row_reference)) {
1768 gtk_tree_row_reference_free (row_reference);
1771 g_object_unref (header);
1777 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1779 ModestMsgViewWindowPrivate *priv = NULL;
1781 gboolean finished = FALSE;
1782 gboolean retval = FALSE;
1784 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1785 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1787 /* Return inmediatly if there is no header model */
1788 if (!priv->header_model || !priv->row_reference)
1791 path = gtk_tree_row_reference_get_path (priv->row_reference);
1792 while (!finished && gtk_tree_path_prev (path)) {
1796 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1797 gtk_tree_model_get (priv->header_model, &iter,
1798 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1802 if (msg_is_visible (header, priv->is_outbox)) {
1803 GtkTreeRowReference *row_reference;
1804 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1805 /* Read the message & show it */
1806 retval = message_reader (window, priv, header, row_reference);
1807 gtk_tree_row_reference_free (row_reference);
1811 g_object_unref (header);
1815 gtk_tree_path_free (path);
1820 view_msg_cb (ModestMailOperation *mail_op,
1827 ModestMsgViewWindow *self = NULL;
1828 ModestMsgViewWindowPrivate *priv = NULL;
1829 GtkTreeRowReference *row_reference = NULL;
1831 row_reference = (GtkTreeRowReference *) user_data;
1833 gtk_tree_row_reference_free (row_reference);
1837 /* If there was any error */
1838 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1839 gtk_tree_row_reference_free (row_reference);
1843 /* Get the window */
1844 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1845 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1846 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1848 /* Update the row reference */
1849 if (priv->row_reference != NULL) {
1850 gtk_tree_row_reference_free (priv->row_reference);
1851 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1852 if (priv->next_row_reference != NULL) {
1853 gtk_tree_row_reference_free (priv->next_row_reference);
1855 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1856 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1859 /* Mark header as read */
1860 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1861 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1863 /* Set new message */
1864 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1865 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1866 modest_msg_view_window_update_priority (self);
1867 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1868 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1871 /* Set the new message uid of the window */
1872 if (priv->msg_uid) {
1873 g_free (priv->msg_uid);
1874 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1877 /* Notify the observers */
1878 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1879 0, priv->header_model, priv->row_reference);
1882 g_object_unref (self);
1883 gtk_tree_row_reference_free (row_reference);
1887 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1889 ModestMsgViewWindowPrivate *priv;
1891 TnyFolderType folder_type;
1893 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1895 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1897 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1901 folder = tny_msg_get_folder (msg);
1903 folder_type = modest_tny_folder_guess_folder_type (folder);
1904 g_object_unref (folder);
1906 g_object_unref (msg);
1914 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1916 ModestMsgViewWindowPrivate *priv;
1917 TnyHeader *header = NULL;
1918 TnyHeaderFlags flags = 0;
1920 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1922 if (priv->header_model && priv->row_reference) {
1924 GtkTreePath *path = NULL;
1926 path = gtk_tree_row_reference_get_path (priv->row_reference);
1927 g_return_if_fail (path != NULL);
1928 gtk_tree_model_get_iter (priv->header_model,
1930 gtk_tree_row_reference_get_path (priv->row_reference));
1932 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1934 gtk_tree_path_free (path);
1937 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1939 header = tny_msg_get_header (msg);
1940 g_object_unref (msg);
1945 flags = tny_header_get_flags (header);
1946 g_object_unref(G_OBJECT(header));
1949 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1954 toolbar_resize (ModestMsgViewWindow *self)
1956 ModestMsgViewWindowPrivate *priv = NULL;
1957 ModestWindowPrivate *parent_priv = NULL;
1959 gint static_button_size;
1960 ModestWindowMgr *mgr;
1962 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1963 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1964 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1966 mgr = modest_runtime_get_window_mgr ();
1967 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
1969 if (parent_priv->toolbar) {
1970 /* left size buttons */
1971 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
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/ToolbarMessageMoveTo");
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/ToolbarDeleteMessage");
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);
1983 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1984 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1985 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1986 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1988 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
1989 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1990 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1991 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1992 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1993 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1994 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1995 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2001 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2003 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2004 ModestWindowPrivate *parent_priv;
2005 ModestWindowMgr *mgr;
2006 gboolean is_fullscreen;
2007 GtkAction *fs_toggle_action;
2010 mgr = modest_runtime_get_window_mgr ();
2011 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2013 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2015 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2016 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2017 if (is_fullscreen != active) {
2018 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2020 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2028 modest_msg_view_window_show_toolbar (ModestWindow *self,
2029 gboolean show_toolbar)
2031 ModestMsgViewWindowPrivate *priv = NULL;
2032 ModestWindowPrivate *parent_priv;
2033 GtkWidget *reply_button = NULL, *menu = NULL;
2034 GtkWidget *placeholder = NULL;
2036 const gchar *action_name;
2039 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2040 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2042 /* Set optimized view status */
2043 priv->optimized_view = !show_toolbar;
2045 if (!parent_priv->toolbar) {
2046 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2048 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2050 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2051 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2052 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2053 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2054 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2056 /* Add ProgressBar (Transfer toolbar) */
2057 priv->progress_bar = modest_progress_bar_new ();
2058 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2059 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2060 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2061 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2062 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2064 /* Connect cancel 'clicked' signal to abort progress mode */
2065 g_signal_connect(priv->cancel_toolitem, "clicked",
2066 G_CALLBACK(cancel_progressbar),
2069 /* Add it to the observers list */
2070 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2073 hildon_window_add_toolbar (HILDON_WINDOW (self),
2074 GTK_TOOLBAR (parent_priv->toolbar));
2076 /* Set reply button tap and hold menu */
2077 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2078 "/ToolBar/ToolbarMessageReply");
2079 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2080 "/ToolbarReplyCSM");
2081 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2085 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2086 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2087 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2089 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2090 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2091 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2093 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2096 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2097 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2100 /* Update also the actions (to update the toggles in the
2101 menus), we have to do it manually because some other window
2102 of the same time could have changed it (remember that the
2103 toolbar fullscreen mode is shared by all the windows of the
2105 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2106 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2108 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2110 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2111 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2116 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2118 ModestMsgViewWindow *window)
2120 if (!GTK_WIDGET_VISIBLE (window))
2123 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2127 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2129 ModestMsgViewWindowPrivate *priv;
2131 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2132 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2134 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2138 cancel_progressbar (GtkToolButton *toolbutton,
2139 ModestMsgViewWindow *self)
2142 ModestMsgViewWindowPrivate *priv;
2144 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2146 /* Get operation observers and cancel its current operation */
2147 tmp = priv->progress_widgets;
2149 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2150 tmp=g_slist_next(tmp);
2154 observers_empty (ModestMsgViewWindow *self)
2157 ModestMsgViewWindowPrivate *priv;
2158 gboolean is_empty = TRUE;
2159 guint pending_ops = 0;
2161 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2162 tmp = priv->progress_widgets;
2164 /* Check all observers */
2165 while (tmp && is_empty) {
2166 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2167 is_empty = pending_ops == 0;
2169 tmp = g_slist_next(tmp);
2176 on_account_removed (TnyAccountStore *account_store,
2177 TnyAccount *account,
2180 /* Do nothing if it's a transport account, because we only
2181 show the messages of a store account */
2182 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2183 const gchar *parent_acc = NULL;
2184 const gchar *our_acc = NULL;
2186 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2187 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2189 /* Close this window if I'm showing a message of the removed account */
2190 if (strcmp (parent_acc, our_acc) == 0)
2191 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2196 on_mail_operation_started (ModestMailOperation *mail_op,
2199 ModestMsgViewWindow *self;
2200 ModestMailOperationTypeOperation op_type;
2202 ModestMsgViewWindowPrivate *priv;
2203 GObject *source = NULL;
2205 self = MODEST_MSG_VIEW_WINDOW (user_data);
2206 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2207 op_type = modest_mail_operation_get_type_operation (mail_op);
2208 tmp = priv->progress_widgets;
2209 source = modest_mail_operation_get_source(mail_op);
2210 if (G_OBJECT (self) == source) {
2211 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2212 set_toolbar_transfer_mode(self);
2214 modest_progress_object_add_operation (
2215 MODEST_PROGRESS_OBJECT (tmp->data),
2217 tmp = g_slist_next (tmp);
2221 g_object_unref (source);
2225 on_mail_operation_finished (ModestMailOperation *mail_op,
2228 ModestMsgViewWindow *self;
2229 ModestMailOperationTypeOperation op_type;
2231 ModestMsgViewWindowPrivate *priv;
2233 self = MODEST_MSG_VIEW_WINDOW (user_data);
2234 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2235 op_type = modest_mail_operation_get_type_operation (mail_op);
2236 tmp = priv->progress_widgets;
2238 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2240 modest_progress_object_remove_operation (
2241 MODEST_PROGRESS_OBJECT (tmp->data),
2243 tmp = g_slist_next (tmp);
2246 /* If no more operations are being observed, NORMAL mode is enabled again */
2247 if (observers_empty (self)) {
2248 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2254 on_queue_changed (ModestMailOperationQueue *queue,
2255 ModestMailOperation *mail_op,
2256 ModestMailOperationQueueNotification type,
2257 ModestMsgViewWindow *self)
2259 ModestMsgViewWindowPrivate *priv;
2261 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2263 /* If this operations was created by another window, do nothing */
2264 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2267 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2268 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2270 "operation-started",
2271 G_CALLBACK (on_mail_operation_started),
2273 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2275 "operation-finished",
2276 G_CALLBACK (on_mail_operation_finished),
2278 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2279 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2281 "operation-started");
2282 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2284 "operation-finished");
2289 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2291 ModestMsgViewWindowPrivate *priv;
2292 TnyList *selected_attachments = NULL;
2294 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2295 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2297 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2299 return selected_attachments;
2303 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2305 ModestMsgViewWindowPrivate *priv;
2306 const gchar *msg_uid;
2307 gchar *attachment_uid = NULL;
2308 gint attachment_index = 0;
2309 TnyList *attachments;
2311 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2312 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2313 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2315 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2316 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2317 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2318 g_object_unref (attachments);
2320 if (msg_uid && attachment_index >= 0) {
2321 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2324 if (mime_part == NULL) {
2325 gboolean error = FALSE;
2326 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2327 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2329 } else if (tny_list_get_length (selected_attachments) > 1) {
2330 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2334 iter = tny_list_create_iterator (selected_attachments);
2335 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2336 g_object_unref (iter);
2338 g_object_unref (selected_attachments);
2343 g_object_ref (mime_part);
2346 if (tny_mime_part_is_purged (mime_part)) {
2347 g_object_unref (mime_part);
2351 if (!modest_tny_mime_part_is_msg (mime_part)) {
2352 gchar *filepath = NULL;
2353 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2354 const gchar *content_type;
2355 gboolean show_error_banner = FALSE;
2357 TnyFsStream *temp_stream = NULL;
2358 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2361 if (temp_stream != NULL) {
2362 content_type = tny_mime_part_get_content_type (mime_part);
2363 if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2364 /* make the file read-only */
2365 if (g_chmod(filepath, 0444) != 0)
2366 g_warning ("%s: failed to set file '%s' to read-only: %s",
2367 __FUNCTION__, filepath, strerror(errno));
2369 modest_platform_activate_file (filepath, content_type);
2371 /* error while saving attachment, maybe cerm_device_memory_full */
2372 show_error_banner = TRUE;
2374 g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2378 g_object_unref (temp_stream);
2380 /* NOTE: files in the temporary area will be automatically
2381 * cleaned after some time if they are no longer in use */
2383 if (filepath != NULL) {
2384 /* the file may already exist but it isn't writable,
2385 * let's try to open it anyway */
2386 content_type = tny_mime_part_get_content_type (mime_part);
2387 modest_platform_activate_file (filepath, content_type);
2390 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2391 show_error_banner = TRUE;
2394 if (show_error_banner)
2395 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2397 /* message attachment */
2398 TnyHeader *header = NULL;
2399 ModestWindowMgr *mgr;
2400 ModestWindow *msg_win = NULL;
2403 header = tny_msg_get_header (TNY_MSG (mime_part));
2404 mgr = modest_runtime_get_window_mgr ();
2405 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2408 if (msg_win) /* there is already a window for this uid; top it */
2409 gtk_window_present (GTK_WINDOW(msg_win));
2411 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2412 * thus, we don't do anything */
2413 g_warning ("window for is already being created");
2415 /* it's not found, so create a new window for it */
2416 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2417 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2419 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2420 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2421 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2422 modest_window_get_zoom (MODEST_WINDOW (window)));
2423 modest_window_mgr_register_window (mgr, msg_win);
2424 gtk_widget_show_all (GTK_WIDGET (msg_win));
2427 g_object_unref (mime_part);
2443 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2444 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2445 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2446 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2449 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2453 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2454 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2455 g_free (pair->filename);
2456 g_object_unref (pair->part);
2457 g_slice_free (SaveMimePartPair, pair);
2459 g_list_free (info->pairs);
2462 gtk_widget_destroy (info->banner);
2463 g_slice_free (SaveMimePartInfo, info);
2468 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2470 if (info->pairs != NULL) {
2471 save_mime_part_to_file (info);
2474 result = info->result;
2476 /* This is a GDK lock because we are an idle callback and
2477 * hildon_banner_show_information is or does Gtk+ code */
2479 gdk_threads_enter (); /* CHECKED */
2480 save_mime_part_info_free (info, TRUE);
2482 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2484 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2486 gdk_threads_leave (); /* CHECKED */
2493 save_mime_part_to_file (SaveMimePartInfo *info)
2495 GnomeVFSResult result;
2496 GnomeVFSHandle *handle;
2498 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2499 gboolean decode_result = TRUE;
2501 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2502 if (result == GNOME_VFS_OK) {
2503 stream = tny_vfs_stream_new (handle);
2504 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2505 decode_result = FALSE;
2507 g_object_unref (G_OBJECT (stream));
2508 g_object_unref (pair->part);
2509 g_slice_free (SaveMimePartPair, pair);
2510 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2511 info->result = decode_result;
2513 save_mime_part_info_free (info, FALSE);
2514 info->result = 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");