1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.h>
42 #include <modest-attachments-view.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include "modest-progress-bar-widget.h"
52 #include "modest-defs.h"
53 #include "modest-hildon-includes.h"
54 #include "modest-ui-dimming-manager.h"
55 #include <gdk/gdkkeysyms.h>
56 #include <modest-tny-account.h>
57 #include <modest-mime-part-view.h>
58 #include <modest-isearch-view.h>
61 #include <glib/gstdio.h>
63 #define DEFAULT_FOLDER "MyDocs/.documents"
65 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
66 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
67 static void modest_header_view_observer_init(
68 ModestHeaderViewObserverIface *iface_class);
69 static void modest_msg_view_window_finalize (GObject *obj);
70 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
72 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
73 ModestMsgViewWindow *obj);
74 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
75 ModestMsgViewWindow *obj);
77 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
78 static void modest_msg_view_window_set_zoom (ModestWindow *window,
80 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
81 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
82 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
83 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
86 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
87 GdkEventWindowState *event,
89 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
91 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
92 gboolean show_toolbar);
94 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
96 ModestMsgViewWindow *window);
97 void modest_msg_view_window_on_row_changed(
98 GtkTreeModel *header_model,
101 ModestMsgViewWindow *window);
103 void modest_msg_view_window_on_row_deleted(
104 GtkTreeModel *header_model,
106 ModestMsgViewWindow *window);
108 void modest_msg_view_window_on_row_inserted(
109 GtkTreeModel *header_model,
110 GtkTreePath *tree_path,
111 GtkTreeIter *tree_iter,
112 ModestMsgViewWindow *window);
114 void modest_msg_view_window_on_row_reordered(
115 GtkTreeModel *header_model,
119 ModestMsgViewWindow *window);
121 void modest_msg_view_window_update_model_replaced(
122 ModestHeaderViewObserver *window,
124 const gchar *tny_folder_id);
126 static void cancel_progressbar (GtkToolButton *toolbutton,
127 ModestMsgViewWindow *self);
129 static void on_queue_changed (ModestMailOperationQueue *queue,
130 ModestMailOperation *mail_op,
131 ModestMailOperationQueueNotification type,
132 ModestMsgViewWindow *self);
134 static void on_account_removed (TnyAccountStore *account_store,
138 static void on_move_focus (GtkWidget *widget,
139 GtkDirectionType direction,
142 static void view_msg_cb (ModestMailOperation *mail_op,
149 static void set_toolbar_mode (ModestMsgViewWindow *self,
150 ModestToolBarModes mode);
152 static void update_window_title (ModestMsgViewWindow *window);
154 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
155 static void init_window (ModestMsgViewWindow *obj);
157 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
160 /* list my signals */
166 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
167 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
168 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
171 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
172 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
173 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
174 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
175 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
176 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
177 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
180 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
181 struct _ModestMsgViewWindowPrivate {
184 GtkWidget *main_scroll;
185 GtkWidget *find_toolbar;
188 /* Progress observers */
189 GtkWidget *progress_bar;
190 GSList *progress_widgets;
193 GtkWidget *progress_toolitem;
194 GtkWidget *cancel_toolitem;
195 GtkWidget *prev_toolitem;
196 GtkWidget *next_toolitem;
197 ModestToolBarModes current_toolbar_mode;
199 /* Optimized view enabled */
200 gboolean optimized_view;
202 /* Whether this was created via the *_new_for_search_result() function. */
203 gboolean is_search_result;
205 /* Whether the message is in outbox */
208 /* A reference to the @model of the header view
209 * to allow selecting previous/next messages,
210 * if the message is currently selected in the header view.
212 const gchar *header_folder_id;
213 GtkTreeModel *header_model;
214 GtkTreeRowReference *row_reference;
215 GtkTreeRowReference *next_row_reference;
217 gulong clipboard_change_handler;
218 gulong queue_change_handler;
219 gulong account_removed_handler;
220 gulong row_changed_handler;
221 gulong row_deleted_handler;
222 gulong row_inserted_handler;
223 gulong rows_reordered_handler;
226 GtkWidget *remove_attachment_banner;
228 guint progress_bar_timeout;
235 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
236 MODEST_TYPE_MSG_VIEW_WINDOW, \
237 ModestMsgViewWindowPrivate))
239 static GtkWindowClass *parent_class = NULL;
241 /* uncomment the following if you have defined any signals */
242 static guint signals[LAST_SIGNAL] = {0};
245 modest_msg_view_window_get_type (void)
247 static GType my_type = 0;
249 static const GTypeInfo my_info = {
250 sizeof(ModestMsgViewWindowClass),
251 NULL, /* base init */
252 NULL, /* base finalize */
253 (GClassInitFunc) modest_msg_view_window_class_init,
254 NULL, /* class finalize */
255 NULL, /* class data */
256 sizeof(ModestMsgViewWindow),
258 (GInstanceInitFunc) modest_msg_view_window_init,
261 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
262 "ModestMsgViewWindow",
265 static const GInterfaceInfo modest_header_view_observer_info =
267 (GInterfaceInitFunc) modest_header_view_observer_init,
268 NULL, /* interface_finalize */
269 NULL /* interface_data */
272 g_type_add_interface_static (my_type,
273 MODEST_TYPE_HEADER_VIEW_OBSERVER,
274 &modest_header_view_observer_info);
280 save_state (ModestWindow *self)
282 modest_widget_memory_save (modest_runtime_get_conf (),
284 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
289 restore_settings (ModestMsgViewWindow *self)
292 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
295 conf = modest_runtime_get_conf ();
296 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
297 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
298 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
299 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
300 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
301 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
302 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
303 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
304 modest_widget_memory_restore (conf,
306 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
310 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
312 GObjectClass *gobject_class;
313 ModestWindowClass *modest_window_class;
314 gobject_class = (GObjectClass*) klass;
315 modest_window_class = (ModestWindowClass *) klass;
317 parent_class = g_type_class_peek_parent (klass);
318 gobject_class->finalize = modest_msg_view_window_finalize;
320 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
321 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
322 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
323 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
324 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
325 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
327 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
329 modest_window_class->save_state_func = save_state;
331 signals[MSG_CHANGED_SIGNAL] =
332 g_signal_new ("msg-changed",
333 G_TYPE_FROM_CLASS (gobject_class),
335 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
337 modest_marshal_VOID__POINTER_POINTER,
338 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
341 static void modest_header_view_observer_init(
342 ModestHeaderViewObserverIface *iface_class)
344 iface_class->update_func = modest_msg_view_window_update_model_replaced;
348 modest_msg_view_window_init (ModestMsgViewWindow *obj)
350 ModestMsgViewWindowPrivate *priv;
351 ModestWindowPrivate *parent_priv = NULL;
352 GtkActionGroup *action_group = NULL;
353 GError *error = NULL;
354 GdkPixbuf *window_icon;
356 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
357 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
358 parent_priv->ui_manager = gtk_ui_manager_new();
360 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
361 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
363 /* Add common actions */
364 gtk_action_group_add_actions (action_group,
365 modest_action_entries,
366 G_N_ELEMENTS (modest_action_entries),
368 gtk_action_group_add_toggle_actions (action_group,
369 modest_toggle_action_entries,
370 G_N_ELEMENTS (modest_toggle_action_entries),
372 gtk_action_group_add_toggle_actions (action_group,
373 msg_view_toggle_action_entries,
374 G_N_ELEMENTS (msg_view_toggle_action_entries),
376 gtk_action_group_add_radio_actions (action_group,
377 msg_view_zoom_action_entries,
378 G_N_ELEMENTS (msg_view_zoom_action_entries),
380 G_CALLBACK (modest_ui_actions_on_change_zoom),
383 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
384 g_object_unref (action_group);
386 /* Load the UI definition */
387 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
390 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
391 g_error_free (error);
396 /* Add accelerators */
397 gtk_window_add_accel_group (GTK_WINDOW (obj),
398 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
400 priv->is_search_result = FALSE;
401 priv->is_outbox = FALSE;
403 priv->msg_view = NULL;
404 priv->header_model = NULL;
405 priv->header_folder_id = NULL;
406 priv->clipboard_change_handler = 0;
407 priv->queue_change_handler = 0;
408 priv->account_removed_handler = 0;
409 priv->row_changed_handler = 0;
410 priv->row_deleted_handler = 0;
411 priv->row_inserted_handler = 0;
412 priv->rows_reordered_handler = 0;
413 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
415 priv->optimized_view = FALSE;
416 priv->progress_bar_timeout = 0;
417 priv->purge_timeout = 0;
418 priv->remove_attachment_banner = NULL;
419 priv->msg_uid = NULL;
421 priv->sighandlers = NULL;
424 init_window (MODEST_MSG_VIEW_WINDOW(obj));
426 /* Set window icon */
427 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
429 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
430 g_object_unref (window_icon);
433 hildon_program_add_window (hildon_program_get_instance(),
436 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
437 GTK_WINDOW(obj),"applications_email_viewer");
442 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
444 ModestMsgViewWindowPrivate *priv = NULL;
446 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
448 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
450 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
452 if (priv->progress_bar_timeout > 0) {
453 g_source_remove (priv->progress_bar_timeout);
454 priv->progress_bar_timeout = 0;
461 set_toolbar_mode (ModestMsgViewWindow *self,
462 ModestToolBarModes mode)
464 ModestWindowPrivate *parent_priv;
465 ModestMsgViewWindowPrivate *priv;
466 /* GtkWidget *widget = NULL; */
468 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
470 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
473 /* Sets current toolbar mode */
474 priv->current_toolbar_mode = mode;
476 /* Update window dimming state */
477 modest_ui_actions_check_window_dimming_rules (MODEST_WINDOW (self));
480 case TOOLBAR_MODE_NORMAL:
481 if (priv->progress_toolitem) {
482 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
483 gtk_widget_hide (priv->progress_toolitem);
486 if (priv->progress_bar)
487 gtk_widget_hide (priv->progress_bar);
489 if (priv->cancel_toolitem)
490 gtk_widget_hide (priv->cancel_toolitem);
492 if (priv->prev_toolitem)
493 gtk_widget_show (priv->prev_toolitem);
495 if (priv->next_toolitem)
496 gtk_widget_show (priv->next_toolitem);
498 /* Hide toolbar if optimized view is enabled */
499 if (priv->optimized_view) {
500 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
501 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
505 case TOOLBAR_MODE_TRANSFER:
506 if (priv->prev_toolitem)
507 gtk_widget_hide (priv->prev_toolitem);
509 if (priv->next_toolitem)
510 gtk_widget_hide (priv->next_toolitem);
512 if (priv->progress_bar)
513 gtk_widget_show (priv->progress_bar);
515 if (priv->progress_toolitem) {
516 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
517 gtk_widget_show (priv->progress_toolitem);
520 if (priv->cancel_toolitem)
521 gtk_widget_show (priv->cancel_toolitem);
523 /* Show toolbar if it's hiden (optimized view ) */
524 if (priv->optimized_view) {
525 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
526 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
531 g_return_if_reached ();
538 init_window (ModestMsgViewWindow *obj)
540 GtkWidget *main_vbox;
541 ModestMsgViewWindowPrivate *priv;
542 ModestWindowPrivate *parent_priv;
544 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
545 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
547 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
548 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
549 main_vbox = gtk_vbox_new (FALSE, 6);
551 #ifdef MODEST_USE_MOZEMBED
552 priv->main_scroll = priv->msg_view;
553 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
555 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
556 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
558 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
559 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
560 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
562 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
563 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
565 priv->find_toolbar = hildon_find_toolbar_new (NULL);
566 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
567 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
569 gtk_widget_show_all (GTK_WIDGET(main_vbox));
573 modest_msg_view_window_disconnect_signals (ModestWindow *self)
575 ModestMsgViewWindowPrivate *priv;
576 ModestHeaderView *header_view = NULL;
577 ModestWindow *main_window = NULL;
579 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
581 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
582 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
583 priv->clipboard_change_handler))
584 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
585 priv->clipboard_change_handler);
587 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
588 priv->queue_change_handler))
589 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
590 priv->queue_change_handler);
592 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
593 priv->account_removed_handler))
594 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
595 priv->account_removed_handler);
597 if (priv->header_model) {
598 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
599 priv->row_changed_handler))
600 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
601 priv->row_changed_handler);
603 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
604 priv->row_deleted_handler))
605 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
606 priv->row_deleted_handler);
608 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
609 priv->row_inserted_handler))
610 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
611 priv->row_inserted_handler);
613 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
614 priv->rows_reordered_handler))
615 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
616 priv->rows_reordered_handler);
619 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
620 priv->sighandlers = NULL;
622 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
623 FALSE); /* don't create */
627 header_view = MODEST_HEADER_VIEW(
628 modest_main_window_get_child_widget(
629 MODEST_MAIN_WINDOW(main_window),
630 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
631 if (header_view == NULL)
634 modest_header_view_remove_observer(header_view,
635 MODEST_HEADER_VIEW_OBSERVER(self));
639 modest_msg_view_window_finalize (GObject *obj)
641 ModestMsgViewWindowPrivate *priv;
643 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
645 /* Sanity check: shouldn't be needed, the window mgr should
646 call this function before */
647 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
649 if (priv->header_model != NULL) {
650 g_object_unref (priv->header_model);
651 priv->header_model = NULL;
654 if (priv->progress_bar_timeout > 0) {
655 g_source_remove (priv->progress_bar_timeout);
656 priv->progress_bar_timeout = 0;
659 if (priv->remove_attachment_banner) {
660 gtk_widget_destroy (priv->remove_attachment_banner);
661 g_object_unref (priv->remove_attachment_banner);
662 priv->remove_attachment_banner = NULL;
665 if (priv->purge_timeout > 0) {
666 g_source_remove (priv->purge_timeout);
667 priv->purge_timeout = 0;
670 if (priv->row_reference) {
671 gtk_tree_row_reference_free (priv->row_reference);
672 priv->row_reference = NULL;
675 if (priv->next_row_reference) {
676 gtk_tree_row_reference_free (priv->next_row_reference);
677 priv->next_row_reference = NULL;
681 g_free (priv->msg_uid);
682 priv->msg_uid = NULL;
685 G_OBJECT_CLASS(parent_class)->finalize (obj);
689 select_next_valid_row (GtkTreeModel *model,
690 GtkTreeRowReference **row_reference,
693 GtkTreeIter tmp_iter;
694 GtkTreePath *path, *next;
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);
724 /* TODO: This should be in _init(), with the parameters as properties. */
726 modest_msg_view_window_construct (ModestMsgViewWindow *self,
727 const gchar *modest_account_name,
728 const gchar *msg_uid)
731 ModestMsgViewWindowPrivate *priv = NULL;
732 ModestWindowPrivate *parent_priv = NULL;
733 ModestDimmingRulesGroup *window_rules_group = NULL;
734 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
736 obj = G_OBJECT (self);
737 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
738 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
740 priv->msg_uid = g_strdup (msg_uid);
743 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
744 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
745 gtk_widget_show (parent_priv->menubar);
746 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
748 window_rules_group = modest_dimming_rules_group_new ("ModestWindowDimmingRules", TRUE);
749 clipboard_rules_group = modest_dimming_rules_group_new ("ModestClipboardDimmingRules", FALSE);
751 /* Add common dimming rules */
752 modest_dimming_rules_group_add_rules (window_rules_group,
753 modest_msg_view_toolbar_dimming_entries,
754 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
755 MODEST_WINDOW (self));
756 modest_dimming_rules_group_add_rules (window_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 (clipboard_rules_group,
761 modest_msg_view_clipboard_dimming_entries,
762 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
763 MODEST_WINDOW (self));
765 /* Insert dimming rules group for this window */
766 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, window_rules_group);
767 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
768 g_object_unref (window_rules_group);
769 g_object_unref (clipboard_rules_group);
771 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
773 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
775 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);
776 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
777 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
778 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
779 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
780 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
781 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
782 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
783 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
784 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
785 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
787 g_signal_connect (G_OBJECT (obj), "key-release-event",
788 G_CALLBACK (modest_msg_view_window_key_event),
791 g_signal_connect (G_OBJECT (obj), "key-press-event",
792 G_CALLBACK (modest_msg_view_window_key_event),
795 g_signal_connect (G_OBJECT (obj), "window-state-event",
796 G_CALLBACK (modest_msg_view_window_window_state_event),
799 g_signal_connect (G_OBJECT (obj), "move-focus",
800 G_CALLBACK (on_move_focus), obj);
802 /* Mail Operation Queue */
803 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
805 G_CALLBACK (on_queue_changed),
808 /* Account manager */
809 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
811 G_CALLBACK(on_account_removed),
814 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
816 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
817 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
818 priv->last_search = NULL;
820 /* Init the clipboard actions dim status */
821 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
823 gtk_widget_show_all (GTK_WIDGET (obj));
824 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
829 /* FIXME: parameter checks */
831 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
832 const gchar *modest_account_name,
833 const gchar *msg_uid,
835 GtkTreeRowReference *row_reference)
837 ModestMsgViewWindow *window = NULL;
838 ModestMsgViewWindowPrivate *priv = NULL;
839 TnyFolder *header_folder = NULL;
840 ModestHeaderView *header_view = NULL;
841 ModestWindow *main_window = NULL;
842 ModestWindowMgr *mgr = NULL;
844 mgr = modest_runtime_get_window_mgr ();
845 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
846 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
848 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
850 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
852 /* Remember the message list's TreeModel so we can detect changes
853 * and change the list selection when necessary: */
855 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
857 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
858 MODEST_MAIN_WINDOW(main_window),
859 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
862 if (header_view != NULL){
863 header_folder = modest_header_view_get_folder(header_view);
864 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
865 g_assert(header_folder != NULL);
866 priv->header_folder_id = tny_folder_get_id(header_folder);
867 g_assert(priv->header_folder_id != NULL);
868 g_object_unref(header_folder);
871 priv->header_model = g_object_ref(model);
873 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
874 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
875 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
877 priv->row_reference = NULL;
878 priv->next_row_reference = NULL;
881 priv->row_changed_handler = g_signal_connect(
882 GTK_TREE_MODEL(model), "row-changed",
883 G_CALLBACK(modest_msg_view_window_on_row_changed),
885 priv->row_deleted_handler = g_signal_connect(
886 GTK_TREE_MODEL(model), "row-deleted",
887 G_CALLBACK(modest_msg_view_window_on_row_deleted),
889 priv->row_inserted_handler = g_signal_connect (
890 GTK_TREE_MODEL(model), "row-inserted",
891 G_CALLBACK(modest_msg_view_window_on_row_inserted),
893 priv->rows_reordered_handler = g_signal_connect(
894 GTK_TREE_MODEL(model), "rows-reordered",
895 G_CALLBACK(modest_msg_view_window_on_row_reordered),
898 if (header_view != NULL){
899 modest_header_view_add_observer(header_view,
900 MODEST_HEADER_VIEW_OBSERVER(window));
903 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
904 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
905 gtk_widget_show_all (GTK_WIDGET (window));
906 modest_msg_view_window_update_priority (window);
908 /* Check window dimming rules */
909 modest_ui_actions_check_window_dimming_rules (MODEST_WINDOW (window));
910 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
912 return MODEST_WINDOW(window);
916 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
917 const gchar *modest_account_name,
918 const gchar *msg_uid)
920 ModestMsgViewWindow *window = NULL;
921 ModestMsgViewWindowPrivate *priv = NULL;
922 ModestWindowMgr *mgr = NULL;
924 mgr = modest_runtime_get_window_mgr ();
925 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
926 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
927 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
929 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
931 /* Remember that this is a search result,
932 * so we can disable some UI appropriately: */
933 priv->is_search_result = TRUE;
935 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
937 update_window_title (window);
938 modest_msg_view_window_update_priority (window);
941 return MODEST_WINDOW(window);
945 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
946 const gchar *modest_account_name,
947 const gchar *msg_uid)
950 ModestMsgViewWindowPrivate *priv;
951 ModestWindowMgr *mgr = NULL;
953 g_return_val_if_fail (msg, NULL);
954 mgr = modest_runtime_get_window_mgr ();
955 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
956 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
957 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
958 modest_account_name, msg_uid);
960 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
961 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
963 /* Check window dimming rules */
964 modest_ui_actions_check_window_dimming_rules (MODEST_WINDOW (obj));
965 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), "ModestClipboardDimmingRules");
967 return MODEST_WINDOW(obj);
970 void modest_msg_view_window_on_row_changed(
971 GtkTreeModel *header_model,
974 ModestMsgViewWindow *window){
975 modest_ui_actions_check_window_dimming_rules (MODEST_WINDOW (window));
978 void modest_msg_view_window_on_row_deleted(
979 GtkTreeModel *header_model,
981 ModestMsgViewWindow *window){
982 modest_ui_actions_check_window_dimming_rules (MODEST_WINDOW (window));
985 /* On insertions we check if the folder still has the message we are
986 * showing or do not. If do not, we do nothing. Which means we are still
987 * not attached to any header folder and thus next/prev buttons are
988 * still dimmed. Once the message that is shown by msg-view is found, the
989 * new model of header-view will be attached and the references will be set.
990 * On each further insertions dimming rules will be checked. However
991 * this requires extra CPU time at least works.
992 * (An message might be deleted from TnyFolder and thus will not be
993 * inserted into the model again for example if it is removed by the
994 * imap server and the header view is refreshed.)
996 void modest_msg_view_window_on_row_inserted(
997 GtkTreeModel *new_model,
998 GtkTreePath *tree_path,
999 GtkTreeIter *tree_iter,
1000 ModestMsgViewWindow *window){
1001 ModestMsgViewWindowPrivate *priv = NULL;
1002 TnyHeader *header = NULL;
1005 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1007 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1009 /* If we already has a model attached then the message shown by
1010 * msg-view is in it, and thus we do not need any actions but
1011 * to check the dimming rules.*/
1012 if(priv->header_model != NULL){
1013 if (priv->row_reference) {
1014 gtk_tree_row_reference_free(priv->next_row_reference);
1015 priv->next_row_reference = gtk_tree_row_reference_copy(
1016 priv->row_reference);
1017 select_next_valid_row (priv->header_model,
1018 &(priv->next_row_reference), FALSE);
1020 modest_ui_actions_check_window_dimming_rules (
1021 MODEST_WINDOW (window));
1025 /* Check if the newly inserted message is the same we are actually
1026 * showing. IF not, we should remain detached from the header model
1027 * and thus prev and next toolbarbuttons should remain dimmed. */
1028 gtk_tree_model_get (new_model, tree_iter,
1029 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1030 uid = modest_tny_folder_get_header_unique_id(header);
1031 if(!g_str_equal(priv->msg_uid, uid)){
1033 g_object_unref(G_OBJECT(header));
1037 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1038 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1039 g_object_unref(G_OBJECT(header));
1043 /* Setup row_reference for the actual msg. */
1044 priv->row_reference = gtk_tree_row_reference_new(
1045 new_model, tree_path);
1046 if(priv->row_reference == NULL){
1047 g_warning("No reference for msg header item.");
1051 /* Attach new_model and connect some callback to it to become able
1052 * to detect changes in header-view. */
1053 priv->header_model = g_object_ref(new_model);
1054 g_signal_connect (new_model, "row-changed",
1055 G_CALLBACK (modest_msg_view_window_on_row_changed),
1057 g_signal_connect (new_model, "row-deleted",
1058 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1060 g_signal_connect (new_model, "rows-reordered",
1061 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1064 /* Now set up next_row_reference. */
1065 priv->next_row_reference = gtk_tree_row_reference_copy(
1066 priv->row_reference);
1067 select_next_valid_row (priv->header_model,
1068 &(priv->next_row_reference), FALSE);
1070 modest_ui_actions_check_window_dimming_rules (MODEST_WINDOW (window));
1073 void modest_msg_view_window_on_row_reordered(
1074 GtkTreeModel *header_model,
1078 ModestMsgViewWindow *window){
1079 modest_ui_actions_check_window_dimming_rules (MODEST_WINDOW (window));
1082 /* The modest_msg_view_window_update_model_replaced implements update
1083 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1084 * actually belongs to the header-view is the same as the TnyFolder of
1085 * the message of msg-view or not. If they are different, there is
1086 * nothing to do. If they are the same, then the model has replaced and
1087 * the reference in msg-view shall be replaced from the old model to
1088 * the new model. In this case the view will be detached from it's
1089 * header folder. From this point the next/prev buttons are dimmed.
1091 void modest_msg_view_window_update_model_replaced(
1092 ModestHeaderViewObserver *observer,
1093 GtkTreeModel *model,
1094 const gchar *tny_folder_id){
1095 ModestMsgViewWindowPrivate *priv = NULL;
1096 ModestMsgViewWindow *window = NULL;
1098 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1099 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1101 window = MODEST_MSG_VIEW_WINDOW(observer);
1102 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1104 /* If there is an other folder in the header-view then we do
1105 * not care about it's model (msg list). Else if the
1106 * header-view shows the folder the msg shown by us is in, we
1107 * shall replace our model reference and make some check. */
1108 if(tny_folder_id == NULL || !g_str_equal(tny_folder_id, priv->header_folder_id))
1111 /* Model is changed(replaced), so we should forget the old
1112 * one. Because there might be other references and there
1113 * might be some change on the model even if we unreferenced
1114 * it, we need to disconnect our signals here. */
1115 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1116 priv->row_changed_handler))
1117 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1118 priv->row_changed_handler);
1119 priv->row_changed_handler = 0;
1120 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1121 priv->row_deleted_handler))
1122 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1123 priv->row_deleted_handler);
1124 priv->row_deleted_handler = 0;
1125 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1126 priv->row_inserted_handler))
1127 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1128 priv->row_inserted_handler);
1129 priv->row_inserted_handler = 0;
1130 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1131 priv->rows_reordered_handler))
1132 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1133 priv->rows_reordered_handler);
1134 priv->rows_reordered_handler = 0;
1135 g_object_unref(priv->header_model);
1136 priv->header_model = NULL;
1137 if (priv->row_reference)
1138 g_object_unref(priv->row_reference);
1139 priv->row_reference = NULL;
1140 if (priv->next_row_reference)
1141 g_object_unref(priv->next_row_reference);
1142 priv->next_row_reference = NULL;
1144 modest_ui_actions_check_window_dimming_rules(MODEST_WINDOW(window));
1146 g_assert(model != NULL);
1148 /* Also we must connect to the new model for row insertions.
1149 * Only for insertions now. We will need other ones only after
1150 * the msg is show by msg-view is added to the new model. */
1151 priv->row_inserted_handler = g_signal_connect (
1152 model, "row-inserted",
1153 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1158 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1160 ModestMsgViewWindowPrivate *priv= NULL;
1162 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1163 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1165 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1169 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1171 ModestMsgViewWindowPrivate *priv= NULL;
1173 TnyHeader *header = NULL;
1174 GtkTreePath *path = NULL;
1177 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1178 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1180 /* If the message was not obtained from a treemodel,
1181 * for instance if it was opened directly by the search UI:
1183 if (priv->header_model == NULL || priv->row_reference == NULL) {
1184 msg = modest_msg_view_window_get_message (self);
1186 header = tny_msg_get_header (msg);
1187 g_object_unref (msg);
1192 /* Get iter of the currently selected message in the header view: */
1193 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1194 * instead of sometimes retrieving it from the header view?
1195 * Then we wouldn't be dependent on the message actually still being selected
1196 * in the header view. murrayc. */
1197 if (!gtk_tree_row_reference_valid (priv->row_reference))
1199 path = gtk_tree_row_reference_get_path (priv->row_reference);
1200 g_return_val_if_fail (path != NULL, NULL);
1201 gtk_tree_model_get_iter (priv->header_model,
1205 /* Get current message header */
1206 gtk_tree_model_get (priv->header_model, &iter,
1207 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1210 gtk_tree_path_free (path);
1215 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1217 ModestMsgViewWindowPrivate *priv;
1219 g_return_val_if_fail (self, NULL);
1221 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1223 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1227 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1229 ModestMsgViewWindowPrivate *priv;
1231 g_return_val_if_fail (self, NULL);
1233 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1235 return (const gchar*) priv->msg_uid;
1239 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1242 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1243 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1244 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1248 is_active = gtk_toggle_action_get_active (toggle);
1251 gtk_widget_show (priv->find_toolbar);
1252 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1254 gtk_widget_hide (priv->find_toolbar);
1257 /* update the toggle buttons status */
1258 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1259 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1260 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1261 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1266 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1267 ModestMsgViewWindow *obj)
1269 GtkToggleAction *toggle;
1270 ModestWindowPrivate *parent_priv;
1271 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1273 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1274 gtk_toggle_action_set_active (toggle, FALSE);
1278 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1279 ModestMsgViewWindow *obj)
1281 gchar *current_search;
1282 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1284 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1285 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1289 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1291 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1292 g_free (current_search);
1293 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1297 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1299 g_free (priv->last_search);
1300 priv->last_search = g_strdup (current_search);
1301 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1304 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1305 g_free (priv->last_search);
1306 priv->last_search = NULL;
1308 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1311 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1312 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1313 g_free (priv->last_search);
1314 priv->last_search = NULL;
1316 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1320 g_free (current_search);
1325 modest_msg_view_window_set_zoom (ModestWindow *window,
1328 ModestMsgViewWindowPrivate *priv;
1329 ModestWindowPrivate *parent_priv;
1330 GtkAction *action = NULL;
1331 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1333 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1335 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1336 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1337 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1339 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1340 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1342 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1346 modest_msg_view_window_get_zoom (ModestWindow *window)
1348 ModestMsgViewWindowPrivate *priv;
1350 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1352 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1353 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1357 modest_msg_view_window_zoom_plus (ModestWindow *window)
1359 ModestWindowPrivate *parent_priv;
1360 GtkRadioAction *zoom_radio_action;
1361 GSList *group, *node;
1363 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1364 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1365 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1367 group = gtk_radio_action_get_group (zoom_radio_action);
1369 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1370 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1374 for (node = group; node != NULL; node = g_slist_next (node)) {
1375 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1376 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1384 modest_msg_view_window_zoom_minus (ModestWindow *window)
1386 ModestWindowPrivate *parent_priv;
1387 GtkRadioAction *zoom_radio_action;
1388 GSList *group, *node;
1390 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1391 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1392 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1394 group = gtk_radio_action_get_group (zoom_radio_action);
1396 for (node = group; node != NULL; node = g_slist_next (node)) {
1397 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1398 if (node->next != NULL) {
1399 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1402 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1412 modest_msg_view_window_key_event (GtkWidget *window,
1418 focus = gtk_window_get_focus (GTK_WINDOW (window));
1420 /* for the find toolbar case */
1421 if (focus && GTK_IS_ENTRY (focus)) {
1422 if (event->keyval == GDK_BackSpace) {
1424 copy = gdk_event_copy ((GdkEvent *) event);
1425 gtk_widget_event (focus, copy);
1426 gdk_event_free (copy);
1431 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1432 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1433 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1434 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1435 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1436 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1437 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1438 gboolean return_value;
1440 if (event->type == GDK_KEY_RELEASE) {
1441 GtkScrollType scroll_type;
1443 switch (event->keyval) {
1446 scroll_type = GTK_SCROLL_STEP_UP; break;
1449 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1451 case GDK_KP_Page_Up:
1452 scroll_type = GTK_SCROLL_PAGE_UP; break;
1454 case GDK_KP_Page_Down:
1455 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1458 scroll_type = GTK_SCROLL_START; break;
1461 scroll_type = GTK_SCROLL_END; break;
1462 default: scroll_type = GTK_SCROLL_NONE;
1465 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",
1466 scroll_type, FALSE, &return_value);
1477 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1480 ModestMsgViewWindowPrivate *priv;
1481 GtkTreeIter tmp_iter;
1482 gboolean is_last_selected;
1484 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1485 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1487 /*if no model (so no rows at all), then virtually we are the last*/
1488 if (!priv->header_model || !priv->row_reference)
1491 path = gtk_tree_row_reference_get_path (priv->row_reference);
1495 is_last_selected = TRUE;
1496 while (is_last_selected) {
1498 gtk_tree_path_next (path);
1499 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1501 gtk_tree_model_get (priv->header_model, &tmp_iter,
1502 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1505 if (msg_is_visible (header, priv->is_outbox))
1506 is_last_selected = FALSE;
1507 g_object_unref(G_OBJECT(header));
1510 gtk_tree_path_free (path);
1511 return is_last_selected;
1515 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1517 ModestMsgViewWindowPrivate *priv;
1519 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1520 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1522 return priv->header_model != NULL;
1526 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1528 ModestMsgViewWindowPrivate *priv;
1530 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1531 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1533 return priv->is_search_result;
1537 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1539 return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1540 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1545 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1548 ModestMsgViewWindowPrivate *priv;
1549 gboolean is_first_selected;
1550 GtkTreeIter tmp_iter;
1551 /* gchar * path_string;*/
1553 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1554 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1556 /*if no model (so no rows at all), then virtually we are the first*/
1557 if (!priv->header_model || !priv->row_reference)
1560 path = gtk_tree_row_reference_get_path (priv->row_reference);
1564 /* path_string = gtk_tree_path_to_string (path);
1565 is_first_selected = strcmp (path_string, "0");
1567 g_free (path_string);
1568 gtk_tree_path_free (path);
1570 return is_first_selected;*/
1572 is_first_selected = TRUE;
1573 while (is_first_selected) {
1575 if(!gtk_tree_path_prev (path))
1577 /* Here the 'if' is needless for logic, but let make sure
1578 * iter is valid for gtk_tree_model_get. */
1579 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1581 gtk_tree_model_get (priv->header_model, &tmp_iter,
1582 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1585 if (msg_is_visible (header, priv->is_outbox))
1586 is_first_selected = FALSE;
1587 g_object_unref(G_OBJECT(header));
1590 gtk_tree_path_free (path);
1591 return is_first_selected;
1595 * Reads the message whose summary item is @header. It takes care of
1596 * several things, among others:
1598 * If the message was not previously downloaded then ask the user
1599 * before downloading. If there is no connection launch the connection
1600 * dialog. Update window dimming rules.
1602 * Returns: TRUE if the mail operation was started, otherwise if the
1603 * user do not want to download the message, or if the user do not
1604 * want to connect, then the operation is not issued
1607 message_reader (ModestMsgViewWindow *window,
1608 ModestMsgViewWindowPrivate *priv,
1610 GtkTreeRowReference *row_reference)
1612 ModestMailOperation *mail_op = NULL;
1613 gboolean already_showing = FALSE;
1614 ModestWindow *msg_window = NULL;
1615 ModestWindowMgr *mgr;
1617 g_return_val_if_fail (row_reference != NULL, FALSE);
1619 mgr = modest_runtime_get_window_mgr ();
1620 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1621 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1624 gtk_window_present (GTK_WINDOW (msg_window));
1625 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1629 /* Msg download completed */
1630 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1631 /* Ask the user if he wants to download the message if
1633 if (!tny_device_is_online (modest_runtime_get_device())) {
1634 TnyFolder *folder = NULL;
1635 GtkResponseType response;
1637 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1638 _("mcen_nc_get_msg"));
1639 if (response == GTK_RESPONSE_CANCEL)
1642 /* Offer the connection dialog if necessary */
1643 folder = tny_header_get_folder (header);
1645 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1646 TNY_FOLDER_STORE (folder))) {
1647 g_object_unref (folder);
1650 g_object_unref (folder);
1655 /* New mail operation */
1656 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(window),
1657 modest_ui_actions_get_msgs_full_error_handler,
1660 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1661 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, row_reference);
1662 g_object_unref (mail_op);
1664 /* Update window dimming rules */
1665 modest_ui_actions_check_window_dimming_rules (MODEST_WINDOW (window));
1671 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1673 ModestMsgViewWindowPrivate *priv;
1674 GtkTreePath *path= NULL;
1675 GtkTreeIter tmp_iter;
1677 gboolean retval = TRUE;
1678 GtkTreeRowReference *row_reference = NULL;
1680 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1681 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1683 if (!priv->row_reference)
1686 /* Update the next row reference if it's not valid. This could
1687 happen if for example the header which it was pointing to,
1688 was deleted. The best place to do it is in the row-deleted
1689 handler but the tinymail model do not work like the glib
1690 tree models and reports the deletion when the row is still
1692 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1693 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1694 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1695 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1698 if (priv->next_row_reference)
1699 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1703 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1705 gtk_tree_model_get_iter (priv->header_model,
1708 gtk_tree_path_free (path);
1710 gtk_tree_model_get (priv->header_model, &tmp_iter,
1711 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1714 /* Read the message & show it */
1715 if (!message_reader (window, priv, header, row_reference)) {
1717 gtk_tree_row_reference_free (row_reference);
1721 g_object_unref (header);
1727 modest_msg_view_window_select_first_message (ModestMsgViewWindow *self)
1729 ModestMsgViewWindowPrivate *priv = NULL;
1730 TnyHeader *header = NULL;
1732 GtkTreePath *path = NULL;
1733 GtkTreeRowReference *row_reference = NULL;
1735 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1736 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1738 /* Check that the model is not empty */
1739 if (!gtk_tree_model_get_iter_first (priv->header_model, &iter))
1742 /* Get the header */
1743 gtk_tree_model_get (priv->header_model,
1745 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1747 g_return_val_if_fail (TNY_IS_HEADER (header), FALSE);
1748 if (!msg_is_visible (header, priv->is_outbox)) {
1749 g_object_unref (header);
1750 return modest_msg_view_window_select_next_message (self);
1753 path = gtk_tree_model_get_path (priv->header_model, &iter);
1754 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1755 gtk_tree_path_free (path);
1757 /* Read the message & show it */
1758 message_reader (self, priv, header, row_reference);
1761 g_object_unref (header);
1767 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1769 ModestMsgViewWindowPrivate *priv = NULL;
1771 GtkTreeRowReference *row_reference = NULL;
1773 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1774 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1776 /* Return inmediatly if there is no header model */
1777 if (!priv->header_model || !priv->row_reference)
1780 path = gtk_tree_row_reference_get_path (priv->row_reference);
1781 while (gtk_tree_path_prev (path)) {
1785 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1786 gtk_tree_model_get (priv->header_model, &iter,
1787 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1791 if (!msg_is_visible (header, priv->is_outbox)) {
1792 g_object_unref (header);
1796 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1797 /* Read the message & show it */
1798 if (!message_reader (window, priv, header, row_reference)) {
1799 gtk_tree_row_reference_free (row_reference);
1800 g_object_unref (header);
1804 gtk_tree_path_free (path);
1805 g_object_unref (header);
1810 gtk_tree_path_free (path);
1815 view_msg_cb (ModestMailOperation *mail_op,
1822 ModestMsgViewWindow *self = NULL;
1823 ModestMsgViewWindowPrivate *priv = NULL;
1824 GtkTreeRowReference *row_reference = NULL;
1827 g_object_unref (self);
1831 /* If there was any error */
1832 row_reference = (GtkTreeRowReference *) user_data;
1833 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1834 gtk_tree_row_reference_free (row_reference);
1838 /* Get the window */
1839 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1840 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1841 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1843 /* Update the row reference */
1844 if (priv->row_reference != NULL) {
1845 gtk_tree_row_reference_free (priv->row_reference);
1846 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1847 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1848 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1849 gtk_tree_row_reference_free (row_reference);
1852 /* Mark header as read */
1853 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1854 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1856 /* Set new message */
1857 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1858 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1859 modest_msg_view_window_update_priority (self);
1860 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1861 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1864 /* Set the new message uid of the window */
1865 if (priv->msg_uid) {
1866 g_free (priv->msg_uid);
1867 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1870 /* Notify the observers */
1871 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1872 0, priv->header_model, priv->row_reference);
1874 /* Free new references */
1875 g_object_unref (self);
1879 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1881 ModestMsgViewWindowPrivate *priv;
1883 TnyFolderType folder_type;
1885 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1887 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1889 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1893 folder = tny_msg_get_folder (msg);
1895 folder_type = tny_folder_get_folder_type (folder);
1896 g_object_unref (folder);
1898 g_object_unref (msg);
1906 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1908 ModestMsgViewWindowPrivate *priv;
1909 TnyHeader *header = NULL;
1910 TnyHeaderFlags flags = 0;
1912 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1914 if (priv->header_model && priv->row_reference) {
1916 GtkTreePath *path = NULL;
1918 path = gtk_tree_row_reference_get_path (priv->row_reference);
1919 g_return_if_fail (path != NULL);
1920 gtk_tree_model_get_iter (priv->header_model,
1922 gtk_tree_row_reference_get_path (priv->row_reference));
1924 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1926 gtk_tree_path_free (path);
1929 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1931 header = tny_msg_get_header (msg);
1932 g_object_unref (msg);
1937 flags = tny_header_get_flags (header);
1938 g_object_unref(G_OBJECT(header));
1941 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1946 toolbar_resize (ModestMsgViewWindow *self)
1948 ModestMsgViewWindowPrivate *priv = NULL;
1949 ModestWindowPrivate *parent_priv = NULL;
1951 gint static_button_size;
1952 ModestWindowMgr *mgr;
1954 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1955 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1956 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1958 mgr = modest_runtime_get_window_mgr ();
1959 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
1961 if (parent_priv->toolbar) {
1962 /* left size buttons */
1963 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
1964 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1965 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1966 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1967 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
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/ToolbarDeleteMessage");
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/FindInMessage");
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);
1980 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
1981 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1982 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1983 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1984 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1985 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1986 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1987 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1993 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1995 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1996 ModestWindowPrivate *parent_priv;
1997 ModestWindowMgr *mgr;
1998 gboolean is_fullscreen;
1999 GtkAction *fs_toggle_action;
2002 mgr = modest_runtime_get_window_mgr ();
2003 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2005 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2007 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2008 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2009 if (is_fullscreen != active) {
2010 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2012 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2020 modest_msg_view_window_show_toolbar (ModestWindow *self,
2021 gboolean show_toolbar)
2023 ModestMsgViewWindowPrivate *priv = NULL;
2024 ModestWindowPrivate *parent_priv;
2025 GtkWidget *reply_button = NULL, *menu = NULL;
2026 GtkWidget *placeholder = NULL;
2028 const gchar *action_name;
2031 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2032 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2034 /* Set optimized view status */
2035 priv->optimized_view = !show_toolbar;
2037 if (!parent_priv->toolbar) {
2038 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2040 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2042 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2043 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2044 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2045 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2046 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2048 /* Add ProgressBar (Transfer toolbar) */
2049 priv->progress_bar = modest_progress_bar_widget_new ();
2050 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2051 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2052 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2053 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2054 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2056 /* Connect cancel 'clicked' signal to abort progress mode */
2057 g_signal_connect(priv->cancel_toolitem, "clicked",
2058 G_CALLBACK(cancel_progressbar),
2061 /* Add it to the observers list */
2062 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2065 hildon_window_add_toolbar (HILDON_WINDOW (self),
2066 GTK_TOOLBAR (parent_priv->toolbar));
2068 /* Set reply button tap and hold menu */
2069 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2070 "/ToolBar/ToolbarMessageReply");
2071 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2072 "/ToolbarReplyCSM");
2073 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2077 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2078 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2079 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2081 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2082 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2083 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2085 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2088 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2089 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2092 /* Update also the actions (to update the toggles in the
2093 menus), we have to do it manually because some other window
2094 of the same time could have changed it (remember that the
2095 toolbar fullscreen mode is shared by all the windows of the
2097 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2098 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2100 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2102 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2103 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2108 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2110 ModestMsgViewWindow *window)
2112 if (!GTK_WIDGET_VISIBLE (window))
2115 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), "ModestClipboardDimmingRules");
2119 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2121 ModestMsgViewWindowPrivate *priv;
2123 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2124 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2126 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2130 cancel_progressbar (GtkToolButton *toolbutton,
2131 ModestMsgViewWindow *self)
2134 ModestMsgViewWindowPrivate *priv;
2136 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2138 /* Get operation observers and cancel its current operation */
2139 tmp = priv->progress_widgets;
2141 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2142 tmp=g_slist_next(tmp);
2146 observers_empty (ModestMsgViewWindow *self)
2149 ModestMsgViewWindowPrivate *priv;
2150 gboolean is_empty = TRUE;
2151 guint pending_ops = 0;
2153 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2154 tmp = priv->progress_widgets;
2156 /* Check all observers */
2157 while (tmp && is_empty) {
2158 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2159 is_empty = pending_ops == 0;
2161 tmp = g_slist_next(tmp);
2168 on_account_removed (TnyAccountStore *account_store,
2169 TnyAccount *account,
2172 /* Do nothing if it's a transport account, because we only
2173 show the messages of a store account */
2174 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2175 const gchar *parent_acc = NULL;
2176 const gchar *our_acc = NULL;
2178 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2179 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2181 /* Close this window if I'm showing a message of the removed account */
2182 if (strcmp (parent_acc, our_acc) == 0)
2183 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2188 on_mail_operation_started (ModestMailOperation *mail_op,
2191 ModestMsgViewWindow *self;
2192 ModestMailOperationTypeOperation op_type;
2194 ModestMsgViewWindowPrivate *priv;
2195 GObject *source = NULL;
2197 self = MODEST_MSG_VIEW_WINDOW (user_data);
2198 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2199 op_type = modest_mail_operation_get_type_operation (mail_op);
2200 tmp = priv->progress_widgets;
2201 source = modest_mail_operation_get_source(mail_op);
2202 if (G_OBJECT (self) == source) {
2203 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2204 set_toolbar_transfer_mode(self);
2206 modest_progress_object_add_operation (
2207 MODEST_PROGRESS_OBJECT (tmp->data),
2209 tmp = g_slist_next (tmp);
2213 g_object_unref (source);
2217 on_mail_operation_finished (ModestMailOperation *mail_op,
2220 ModestMsgViewWindow *self;
2221 ModestMailOperationTypeOperation op_type;
2223 ModestMsgViewWindowPrivate *priv;
2225 self = MODEST_MSG_VIEW_WINDOW (user_data);
2226 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2227 op_type = modest_mail_operation_get_type_operation (mail_op);
2228 tmp = priv->progress_widgets;
2230 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2232 modest_progress_object_remove_operation (
2233 MODEST_PROGRESS_OBJECT (tmp->data),
2235 tmp = g_slist_next (tmp);
2238 /* If no more operations are being observed, NORMAL mode is enabled again */
2239 if (observers_empty (self)) {
2240 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2246 on_queue_changed (ModestMailOperationQueue *queue,
2247 ModestMailOperation *mail_op,
2248 ModestMailOperationQueueNotification type,
2249 ModestMsgViewWindow *self)
2251 ModestMsgViewWindowPrivate *priv;
2253 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2255 /* If this operations was created by another window, do nothing */
2256 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2259 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2260 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2262 "operation-started",
2263 G_CALLBACK (on_mail_operation_started),
2265 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2267 "operation-finished",
2268 G_CALLBACK (on_mail_operation_finished),
2270 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2271 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2273 "operation-started");
2274 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2276 "operation-finished");
2281 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2283 ModestMsgViewWindowPrivate *priv;
2284 TnyList *selected_attachments = NULL;
2286 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2287 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2289 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2291 return selected_attachments;
2295 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2297 ModestMsgViewWindowPrivate *priv;
2298 const gchar *msg_uid;
2299 gchar *attachment_uid = NULL;
2300 gint attachment_index = 0;
2301 TnyList *attachments;
2303 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2304 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2305 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2307 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2308 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2309 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2310 g_object_unref (attachments);
2312 if (msg_uid && attachment_index >= 0) {
2313 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2316 if (mime_part == NULL) {
2317 gboolean error = FALSE;
2318 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2319 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2321 } else if (tny_list_get_length (selected_attachments) > 1) {
2322 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2326 iter = tny_list_create_iterator (selected_attachments);
2327 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2328 g_object_unref (iter);
2330 g_object_unref (selected_attachments);
2335 g_object_ref (mime_part);
2338 if (tny_mime_part_is_purged (mime_part)) {
2339 g_object_unref (mime_part);
2343 if (!TNY_IS_MSG (mime_part)) {
2344 gchar *filepath = NULL;
2345 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2346 const gchar *content_type;
2347 gboolean show_error_banner = FALSE;
2349 TnyFsStream *temp_stream = NULL;
2350 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2353 if (temp_stream != NULL) {
2354 content_type = tny_mime_part_get_content_type (mime_part);
2355 if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2356 /* make the file read-only */
2357 if (g_chmod(filepath, 0444) != 0)
2358 g_warning ("%s: failed to set file '%s' to read-only: %s",
2359 __FUNCTION__, filepath, strerror(errno));
2361 modest_platform_activate_file (filepath, content_type);
2363 /* error while saving attachment, maybe cerm_device_memory_full */
2364 show_error_banner = TRUE;
2366 g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2370 g_object_unref (temp_stream);
2372 /* NOTE: files in the temporary area will be automatically
2373 * cleaned after some time if they are no longer in use */
2375 if (filepath != NULL) {
2376 /* the file may already exist but it isn't writable,
2377 * let's try to open it anyway */
2378 content_type = tny_mime_part_get_content_type (mime_part);
2379 modest_platform_activate_file (filepath, content_type);
2382 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2383 show_error_banner = TRUE;
2386 if (show_error_banner)
2387 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2389 /* message attachment */
2390 TnyHeader *header = NULL;
2391 ModestWindowMgr *mgr;
2392 ModestWindow *msg_win = NULL;
2395 header = tny_msg_get_header (TNY_MSG (mime_part));
2396 mgr = modest_runtime_get_window_mgr ();
2397 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2400 if (msg_win) /* there is already a window for this uid; top it */
2401 gtk_window_present (GTK_WINDOW(msg_win));
2403 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2404 * thus, we don't do anything */
2405 g_warning ("window for is already being created");
2407 /* it's not found, so create a new window for it */
2408 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2409 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2411 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2412 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2413 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2414 modest_window_get_zoom (MODEST_WINDOW (window)));
2415 modest_window_mgr_register_window (mgr, msg_win);
2416 gtk_widget_show_all (GTK_WIDGET (msg_win));
2419 g_object_unref (mime_part);
2435 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2436 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2437 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2438 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2441 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2445 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2446 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2447 g_free (pair->filename);
2448 g_object_unref (pair->part);
2449 g_slice_free (SaveMimePartPair, pair);
2451 g_list_free (info->pairs);
2454 gtk_widget_destroy (info->banner);
2455 g_object_unref (info->banner);
2456 g_slice_free (SaveMimePartInfo, info);
2461 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2463 if (info->pairs != NULL) {
2464 save_mime_part_to_file (info);
2467 result = info->result;
2469 /* This is a GDK lock because we are an idle callback and
2470 * hildon_banner_show_information is or does Gtk+ code */
2472 gdk_threads_enter (); /* CHECKED */
2473 save_mime_part_info_free (info, TRUE);
2475 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2477 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2479 gdk_threads_leave (); /* CHECKED */
2486 save_mime_part_to_file (SaveMimePartInfo *info)
2488 GnomeVFSResult result;
2489 GnomeVFSHandle *handle;
2491 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2492 gboolean decode_result = TRUE;
2494 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2495 if (result == GNOME_VFS_OK) {
2496 stream = tny_vfs_stream_new (handle);
2497 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2498 decode_result = FALSE;
2500 g_object_unref (G_OBJECT (stream));
2501 g_object_unref (pair->part);
2502 g_slice_free (SaveMimePartPair, pair);
2503 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2504 info->result = decode_result;
2506 save_mime_part_info_free (info, FALSE);
2507 info->result = FALSE;
2510 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2515 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2517 gboolean is_ok = TRUE;
2518 gint replaced_files = 0;
2519 const GList *files = info->pairs;
2522 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2523 SaveMimePartPair *pair = iter->data;
2524 if (modest_utils_file_exists (pair->filename)) {
2528 if (replaced_files) {
2529 GtkWidget *confirm_overwrite_dialog;
2530 const gchar *message = (replaced_files == 1) ?
2531 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2532 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2533 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2536 gtk_widget_destroy (confirm_overwrite_dialog);
2540 save_mime_part_info_free (info, TRUE);
2542 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2543 _CS("sfil_ib_saving"));
2544 info->banner = g_object_ref (banner);
2545 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2546 g_object_unref (banner);
2553 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2555 ModestMsgViewWindowPrivate *priv;
2556 GList *files_to_save = NULL;
2557 GtkWidget *save_dialog = NULL;
2558 gchar *folder = NULL;
2559 gboolean canceled = FALSE;
2560 const gchar *filename = NULL;
2561 gchar *save_multiple_str = NULL;
2563 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2564 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2566 if (mime_parts == NULL) {
2567 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2568 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2571 g_object_ref (mime_parts);
2574 /* prepare dialog */
2575 if (tny_list_get_length (mime_parts) == 1) {
2577 /* only one attachment selected */
2578 iter = tny_list_create_iterator (mime_parts);
2579 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2580 g_object_unref (iter);
2581 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2582 filename = tny_mime_part_get_filename (mime_part);
2584 g_warning ("Tried to save a non-file attachment");
2587 g_object_unref (mime_part);
2589 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2590 tny_list_get_length (mime_parts));
2593 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2594 GTK_FILE_CHOOSER_ACTION_SAVE);
2597 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2598 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2602 if (filename != NULL)
2603 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2606 /* if multiple, set multiple string */
2607 if (save_multiple_str) {
2608 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2612 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2613 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2615 if (!modest_utils_folder_writable (chooser_uri)) {
2616 hildon_banner_show_information
2617 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2621 iter = tny_list_create_iterator (mime_parts);
2622 while (!tny_iterator_is_done (iter)) {
2623 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2625 tny_iterator_next (iter);
2626 if (tny_mime_part_is_attachment (mime_part)) {
2627 SaveMimePartPair *pair;
2629 if ((!tny_iterator_is_done (iter)) &&
2630 (tny_mime_part_get_filename (mime_part) == NULL)) {
2631 g_object_unref (mime_part);
2635 pair = g_slice_new0 (SaveMimePartPair);
2636 if (tny_iterator_is_done (iter)) {
2637 pair->filename = g_strdup (chooser_uri);
2640 g_build_filename (chooser_uri,
2641 tny_mime_part_get_filename (mime_part), NULL);
2643 pair->part = mime_part;
2644 files_to_save = g_list_prepend (files_to_save, pair);
2647 g_object_unref (iter);
2649 g_free (chooser_uri);
2652 gtk_widget_destroy (save_dialog);
2654 g_object_unref (mime_parts);
2656 if (files_to_save != NULL) {
2657 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2658 info->pairs = files_to_save;
2659 info->result = TRUE;
2660 save_mime_parts_to_file_with_checks (info);
2665 show_remove_attachment_information (gpointer userdata)
2667 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2668 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2670 /* We're outside the main lock */
2671 gdk_threads_enter ();
2673 if (priv->remove_attachment_banner != NULL) {
2674 gtk_widget_destroy (priv->remove_attachment_banner);
2675 g_object_unref (priv->remove_attachment_banner);
2678 priv->remove_attachment_banner = g_object_ref (
2679 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2681 gdk_threads_leave ();
2687 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2689 ModestMsgViewWindowPrivate *priv;
2690 TnyList *mime_parts = NULL;
2691 gchar *confirmation_message;
2696 /* TnyFolder *folder; */
2698 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2699 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2702 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2704 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2706 /* Remove already purged messages from mime parts list */
2707 iter = tny_list_create_iterator (mime_parts);
2708 while (!tny_iterator_is_done (iter)) {
2709 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2710 tny_iterator_next (iter);
2711 if (tny_mime_part_is_purged (part)) {
2712 tny_list_remove (mime_parts, (GObject *) part);
2714 g_object_unref (part);
2716 g_object_unref (iter);
2718 if (tny_list_get_length (mime_parts) == 0) {
2719 g_object_unref (mime_parts);
2723 n_attachments = tny_list_get_length (mime_parts);
2724 if (n_attachments == 1) {
2725 const gchar *filename;
2728 iter = tny_list_create_iterator (mime_parts);
2729 part = (TnyMimePart *) tny_iterator_get_current (iter);
2730 g_object_unref (iter);
2731 if (TNY_IS_MSG (part)) {
2733 header = tny_msg_get_header (TNY_MSG (part));
2734 filename = tny_header_get_subject (header);
2735 g_object_unref (header);
2736 if (filename == NULL)
2737 filename = _("mail_va_no_subject");
2739 filename = tny_mime_part_get_filename (TNY_MIME_PART (part));
2741 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2742 g_object_unref (part);
2744 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2745 "mcen_nc_purge_files_text",
2746 n_attachments), n_attachments);
2748 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2749 confirmation_message);
2750 g_free (confirmation_message);
2752 if (response != GTK_RESPONSE_OK) {
2753 g_object_unref (mime_parts);
2757 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2758 /* folder = tny_msg_get_folder (msg); */
2759 /* tny_msg_uncache_attachments (msg); */
2760 /* tny_folder_refresh (folder, NULL); */
2761 /* g_object_unref (folder); */
2763 iter = tny_list_create_iterator (mime_parts);
2764 while (!tny_iterator_is_done (iter)) {
2767 part = (TnyMimePart *) tny_iterator_get_current (iter);
2768 tny_mime_part_set_purged (TNY_MIME_PART (part));
2769 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2770 g_object_unref (part);
2771 tny_iterator_next (iter);
2773 g_object_unref (iter);
2775 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2776 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2777 tny_msg_rewrite_cache (msg);
2778 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2779 g_object_unref (msg);
2781 g_object_unref (mime_parts);
2783 if (priv->purge_timeout > 0) {
2784 g_source_remove (priv->purge_timeout);
2785 priv->purge_timeout = 0;
2788 if (priv->remove_attachment_banner) {
2789 gtk_widget_destroy (priv->remove_attachment_banner);
2790 g_object_unref (priv->remove_attachment_banner);
2791 priv->remove_attachment_banner = NULL;
2799 update_window_title (ModestMsgViewWindow *window)
2801 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2803 TnyHeader *header = NULL;
2804 const gchar *subject = NULL;
2806 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2809 header = tny_msg_get_header (msg);
2810 subject = tny_header_get_subject (header);
2811 g_object_unref (msg);
2814 if ((subject == NULL)||(subject[0] == '\0'))
2815 subject = _("mail_va_no_subject");
2817 gtk_window_set_title (GTK_WINDOW (window), subject);
2821 static void on_move_focus (GtkWidget *widget,
2822 GtkDirectionType direction,
2825 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");