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-widget.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>
60 #include <glib/gstdio.h>
62 #define DEFAULT_FOLDER "MyDocs/.documents"
64 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
65 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
66 static void modest_header_view_observer_init(
67 ModestHeaderViewObserverIface *iface_class);
68 static void modest_msg_view_window_finalize (GObject *obj);
69 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
71 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
72 ModestMsgViewWindow *obj);
73 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
74 ModestMsgViewWindow *obj);
76 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
77 static void modest_msg_view_window_set_zoom (ModestWindow *window,
79 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
80 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
81 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
82 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
85 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
86 GdkEventWindowState *event,
88 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
90 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
91 gboolean show_toolbar);
93 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
95 ModestMsgViewWindow *window);
96 void modest_msg_view_window_on_row_changed(
97 GtkTreeModel *header_model,
100 ModestMsgViewWindow *window);
102 void modest_msg_view_window_on_row_deleted(
103 GtkTreeModel *header_model,
105 ModestMsgViewWindow *window);
107 void modest_msg_view_window_on_row_inserted(
108 GtkTreeModel *header_model,
109 GtkTreePath *tree_path,
110 GtkTreeIter *tree_iter,
111 ModestMsgViewWindow *window);
113 void modest_msg_view_window_on_row_reordered(
114 GtkTreeModel *header_model,
118 ModestMsgViewWindow *window);
120 void modest_msg_view_window_update_model_replaced(
121 ModestHeaderViewObserver *window,
123 const gchar *tny_folder_id);
125 static void cancel_progressbar (GtkToolButton *toolbutton,
126 ModestMsgViewWindow *self);
128 static void on_queue_changed (ModestMailOperationQueue *queue,
129 ModestMailOperation *mail_op,
130 ModestMailOperationQueueNotification type,
131 ModestMsgViewWindow *self);
133 static void on_account_removed (TnyAccountStore *account_store,
137 static void on_move_focus (GtkWidget *widget,
138 GtkDirectionType direction,
141 static void view_msg_cb (ModestMailOperation *mail_op,
148 static void set_toolbar_mode (ModestMsgViewWindow *self,
149 ModestToolBarModes mode);
151 static void update_window_title (ModestMsgViewWindow *window);
153 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
154 static void init_window (ModestMsgViewWindow *obj);
156 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
159 /* list my signals */
165 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
166 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
167 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
170 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
171 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
172 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
173 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
174 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
175 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
176 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
179 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
180 struct _ModestMsgViewWindowPrivate {
183 GtkWidget *main_scroll;
184 GtkWidget *find_toolbar;
187 /* Progress observers */
188 GtkWidget *progress_bar;
189 GSList *progress_widgets;
192 GtkWidget *progress_toolitem;
193 GtkWidget *cancel_toolitem;
194 GtkWidget *prev_toolitem;
195 GtkWidget *next_toolitem;
196 ModestToolBarModes current_toolbar_mode;
198 /* Optimized view enabled */
199 gboolean optimized_view;
201 /* Whether this was created via the *_new_for_search_result() function. */
202 gboolean is_search_result;
204 /* Whether the message is in outbox */
207 /* A reference to the @model of the header view
208 * to allow selecting previous/next messages,
209 * if the message is currently selected in the header view.
211 const gchar *header_folder_id;
212 GtkTreeModel *header_model;
213 GtkTreeRowReference *row_reference;
214 GtkTreeRowReference *next_row_reference;
216 gulong clipboard_change_handler;
217 gulong queue_change_handler;
218 gulong account_removed_handler;
219 gulong row_changed_handler;
220 gulong row_deleted_handler;
221 gulong row_inserted_handler;
222 gulong rows_reordered_handler;
225 GtkWidget *remove_attachment_banner;
227 guint progress_bar_timeout;
234 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
235 MODEST_TYPE_MSG_VIEW_WINDOW, \
236 ModestMsgViewWindowPrivate))
238 static GtkWindowClass *parent_class = NULL;
240 /* uncomment the following if you have defined any signals */
241 static guint signals[LAST_SIGNAL] = {0};
244 modest_msg_view_window_get_type (void)
246 static GType my_type = 0;
248 static const GTypeInfo my_info = {
249 sizeof(ModestMsgViewWindowClass),
250 NULL, /* base init */
251 NULL, /* base finalize */
252 (GClassInitFunc) modest_msg_view_window_class_init,
253 NULL, /* class finalize */
254 NULL, /* class data */
255 sizeof(ModestMsgViewWindow),
257 (GInstanceInitFunc) modest_msg_view_window_init,
260 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
261 "ModestMsgViewWindow",
264 static const GInterfaceInfo modest_header_view_observer_info =
266 (GInterfaceInitFunc) modest_header_view_observer_init,
267 NULL, /* interface_finalize */
268 NULL /* interface_data */
271 g_type_add_interface_static (my_type,
272 MODEST_TYPE_HEADER_VIEW_OBSERVER,
273 &modest_header_view_observer_info);
279 save_state (ModestWindow *self)
281 modest_widget_memory_save (modest_runtime_get_conf (),
283 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
288 restore_settings (ModestMsgViewWindow *self)
291 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
294 conf = modest_runtime_get_conf ();
295 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
296 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
297 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
298 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
299 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
300 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
301 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
302 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
303 modest_widget_memory_restore (conf,
305 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
309 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
311 GObjectClass *gobject_class;
312 ModestWindowClass *modest_window_class;
313 gobject_class = (GObjectClass*) klass;
314 modest_window_class = (ModestWindowClass *) klass;
316 parent_class = g_type_class_peek_parent (klass);
317 gobject_class->finalize = modest_msg_view_window_finalize;
319 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
320 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
321 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
322 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
323 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
324 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
326 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
328 modest_window_class->save_state_func = save_state;
330 signals[MSG_CHANGED_SIGNAL] =
331 g_signal_new ("msg-changed",
332 G_TYPE_FROM_CLASS (gobject_class),
334 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
336 modest_marshal_VOID__POINTER_POINTER,
337 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
340 static void modest_header_view_observer_init(
341 ModestHeaderViewObserverIface *iface_class)
343 iface_class->update_func = modest_msg_view_window_update_model_replaced;
347 modest_msg_view_window_init (ModestMsgViewWindow *obj)
349 ModestMsgViewWindowPrivate *priv;
350 ModestWindowPrivate *parent_priv = NULL;
351 GtkActionGroup *action_group = NULL;
352 GError *error = NULL;
353 GdkPixbuf *window_icon;
355 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
356 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
357 parent_priv->ui_manager = gtk_ui_manager_new();
359 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
360 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
362 /* Add common actions */
363 gtk_action_group_add_actions (action_group,
364 modest_action_entries,
365 G_N_ELEMENTS (modest_action_entries),
367 gtk_action_group_add_toggle_actions (action_group,
368 modest_toggle_action_entries,
369 G_N_ELEMENTS (modest_toggle_action_entries),
371 gtk_action_group_add_toggle_actions (action_group,
372 msg_view_toggle_action_entries,
373 G_N_ELEMENTS (msg_view_toggle_action_entries),
375 gtk_action_group_add_radio_actions (action_group,
376 msg_view_zoom_action_entries,
377 G_N_ELEMENTS (msg_view_zoom_action_entries),
379 G_CALLBACK (modest_ui_actions_on_change_zoom),
382 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
383 g_object_unref (action_group);
385 /* Load the UI definition */
386 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
389 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
390 g_error_free (error);
395 /* Add accelerators */
396 gtk_window_add_accel_group (GTK_WINDOW (obj),
397 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
399 priv->is_search_result = FALSE;
400 priv->is_outbox = FALSE;
402 priv->msg_view = NULL;
403 priv->header_model = NULL;
404 priv->header_folder_id = NULL;
405 priv->clipboard_change_handler = 0;
406 priv->queue_change_handler = 0;
407 priv->account_removed_handler = 0;
408 priv->row_changed_handler = 0;
409 priv->row_deleted_handler = 0;
410 priv->row_inserted_handler = 0;
411 priv->rows_reordered_handler = 0;
412 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
414 priv->optimized_view = FALSE;
415 priv->progress_bar_timeout = 0;
416 priv->purge_timeout = 0;
417 priv->remove_attachment_banner = NULL;
418 priv->msg_uid = NULL;
420 priv->sighandlers = NULL;
423 init_window (MODEST_MSG_VIEW_WINDOW(obj));
425 /* Set window icon */
426 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
428 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
429 g_object_unref (window_icon);
432 hildon_program_add_window (hildon_program_get_instance(),
435 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
436 GTK_WINDOW(obj),"applications_email_viewer");
441 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
443 ModestMsgViewWindowPrivate *priv = NULL;
445 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
447 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
449 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
451 if (priv->progress_bar_timeout > 0) {
452 g_source_remove (priv->progress_bar_timeout);
453 priv->progress_bar_timeout = 0;
460 set_toolbar_mode (ModestMsgViewWindow *self,
461 ModestToolBarModes mode)
463 ModestWindowPrivate *parent_priv;
464 ModestMsgViewWindowPrivate *priv;
465 /* GtkWidget *widget = NULL; */
467 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
469 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
470 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
472 /* Sets current toolbar mode */
473 priv->current_toolbar_mode = mode;
475 /* Update toolbar dimming state */
476 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
479 case TOOLBAR_MODE_NORMAL:
480 if (priv->progress_toolitem) {
481 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
482 gtk_widget_hide (priv->progress_toolitem);
485 if (priv->progress_bar)
486 gtk_widget_hide (priv->progress_bar);
488 if (priv->cancel_toolitem)
489 gtk_widget_hide (priv->cancel_toolitem);
491 if (priv->prev_toolitem)
492 gtk_widget_show (priv->prev_toolitem);
494 if (priv->next_toolitem)
495 gtk_widget_show (priv->next_toolitem);
497 /* Hide toolbar if optimized view is enabled */
498 if (priv->optimized_view) {
499 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
500 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
504 case TOOLBAR_MODE_TRANSFER:
505 if (priv->prev_toolitem)
506 gtk_widget_hide (priv->prev_toolitem);
508 if (priv->next_toolitem)
509 gtk_widget_hide (priv->next_toolitem);
511 if (priv->progress_bar)
512 gtk_widget_show (priv->progress_bar);
514 if (priv->progress_toolitem) {
515 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
516 gtk_widget_show (priv->progress_toolitem);
519 if (priv->cancel_toolitem)
520 gtk_widget_show (priv->cancel_toolitem);
522 /* Show toolbar if it's hiden (optimized view ) */
523 if (priv->optimized_view) {
524 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
525 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
530 g_return_if_reached ();
537 init_window (ModestMsgViewWindow *obj)
539 GtkWidget *main_vbox;
540 ModestMsgViewWindowPrivate *priv;
542 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
544 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
545 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
546 main_vbox = gtk_vbox_new (FALSE, 6);
548 #ifdef MODEST_USE_MOZEMBED
549 priv->main_scroll = priv->msg_view;
550 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
552 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
553 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
555 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
556 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
557 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
559 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
560 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
562 priv->find_toolbar = hildon_find_toolbar_new (NULL);
563 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
564 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
566 gtk_widget_show_all (GTK_WIDGET(main_vbox));
570 modest_msg_view_window_disconnect_signals (ModestWindow *self)
572 ModestMsgViewWindowPrivate *priv;
573 ModestHeaderView *header_view = NULL;
574 ModestWindow *main_window = NULL;
576 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
578 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
579 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
580 priv->clipboard_change_handler))
581 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
582 priv->clipboard_change_handler);
584 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
585 priv->queue_change_handler))
586 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
587 priv->queue_change_handler);
589 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
590 priv->account_removed_handler))
591 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
592 priv->account_removed_handler);
594 if (priv->header_model) {
595 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
596 priv->row_changed_handler))
597 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
598 priv->row_changed_handler);
600 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
601 priv->row_deleted_handler))
602 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
603 priv->row_deleted_handler);
605 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
606 priv->row_inserted_handler))
607 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
608 priv->row_inserted_handler);
610 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
611 priv->rows_reordered_handler))
612 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
613 priv->rows_reordered_handler);
616 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
617 priv->sighandlers = NULL;
619 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
620 FALSE); /* don't create */
624 header_view = MODEST_HEADER_VIEW(
625 modest_main_window_get_child_widget(
626 MODEST_MAIN_WINDOW(main_window),
627 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
628 if (header_view == NULL)
631 modest_header_view_remove_observer(header_view,
632 MODEST_HEADER_VIEW_OBSERVER(self));
636 modest_msg_view_window_finalize (GObject *obj)
638 ModestMsgViewWindowPrivate *priv;
640 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
642 /* Sanity check: shouldn't be needed, the window mgr should
643 call this function before */
644 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
646 if (priv->header_model != NULL) {
647 g_object_unref (priv->header_model);
648 priv->header_model = NULL;
651 if (priv->progress_bar_timeout > 0) {
652 g_source_remove (priv->progress_bar_timeout);
653 priv->progress_bar_timeout = 0;
656 if (priv->remove_attachment_banner) {
657 gtk_widget_destroy (priv->remove_attachment_banner);
658 g_object_unref (priv->remove_attachment_banner);
659 priv->remove_attachment_banner = NULL;
662 if (priv->purge_timeout > 0) {
663 g_source_remove (priv->purge_timeout);
664 priv->purge_timeout = 0;
667 if (priv->row_reference) {
668 gtk_tree_row_reference_free (priv->row_reference);
669 priv->row_reference = NULL;
672 if (priv->next_row_reference) {
673 gtk_tree_row_reference_free (priv->next_row_reference);
674 priv->next_row_reference = NULL;
678 g_free (priv->msg_uid);
679 priv->msg_uid = NULL;
682 G_OBJECT_CLASS(parent_class)->finalize (obj);
686 select_next_valid_row (GtkTreeModel *model,
687 GtkTreeRowReference **row_reference,
690 GtkTreeIter tmp_iter;
691 GtkTreePath *path, *next;
692 gboolean retval = FALSE;
694 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
696 path = gtk_tree_row_reference_get_path (*row_reference);
697 gtk_tree_model_get_iter (model, &tmp_iter, path);
698 gtk_tree_row_reference_free (*row_reference);
699 *row_reference = NULL;
701 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
702 next = gtk_tree_model_get_path (model, &tmp_iter);
703 *row_reference = gtk_tree_row_reference_new (model, next);
705 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
706 next = gtk_tree_model_get_path (model, &tmp_iter);
708 /* Ensure that we are not selecting the same */
709 if (gtk_tree_path_compare (path, next) != 0) {
710 *row_reference = gtk_tree_row_reference_new (model, next);
716 gtk_tree_path_free (path);
721 /* TODO: This should be in _init(), with the parameters as properties. */
723 modest_msg_view_window_construct (ModestMsgViewWindow *self,
724 const gchar *modest_account_name,
725 const gchar *msg_uid)
728 ModestMsgViewWindowPrivate *priv = NULL;
729 ModestWindowPrivate *parent_priv = NULL;
730 ModestDimmingRulesGroup *menu_rules_group = NULL;
731 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
732 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
734 obj = G_OBJECT (self);
735 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
736 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
738 priv->msg_uid = g_strdup (msg_uid);
741 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
742 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
743 gtk_widget_show (parent_priv->menubar);
744 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
746 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
747 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
748 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
750 /* Add common dimming rules */
751 modest_dimming_rules_group_add_rules (menu_rules_group,
752 modest_msg_view_menu_dimming_entries,
753 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
754 MODEST_WINDOW (self));
755 modest_dimming_rules_group_add_rules (toolbar_rules_group,
756 modest_msg_view_toolbar_dimming_entries,
757 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
758 MODEST_WINDOW (self));
759 modest_dimming_rules_group_add_rules (clipboard_rules_group,
760 modest_msg_view_clipboard_dimming_entries,
761 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
762 MODEST_WINDOW (self));
764 /* Insert dimming rules group for this window */
765 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
766 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
767 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
768 g_object_unref (menu_rules_group);
769 g_object_unref (toolbar_rules_group);
770 g_object_unref (clipboard_rules_group);
772 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
774 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
776 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);
777 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
778 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
779 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
780 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
781 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
782 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
783 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
784 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
785 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
786 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
788 g_signal_connect (G_OBJECT (obj), "key-release-event",
789 G_CALLBACK (modest_msg_view_window_key_event),
792 g_signal_connect (G_OBJECT (obj), "key-press-event",
793 G_CALLBACK (modest_msg_view_window_key_event),
796 g_signal_connect (G_OBJECT (obj), "window-state-event",
797 G_CALLBACK (modest_msg_view_window_window_state_event),
800 g_signal_connect (G_OBJECT (obj), "move-focus",
801 G_CALLBACK (on_move_focus), obj);
803 /* Mail Operation Queue */
804 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
806 G_CALLBACK (on_queue_changed),
809 /* Account manager */
810 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
812 G_CALLBACK(on_account_removed),
815 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
817 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
818 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
819 priv->last_search = NULL;
821 /* Init the clipboard actions dim status */
822 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
824 gtk_widget_show_all (GTK_WIDGET (obj));
825 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
830 /* FIXME: parameter checks */
832 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
833 const gchar *modest_account_name,
834 const gchar *msg_uid,
836 GtkTreeRowReference *row_reference)
838 ModestMsgViewWindow *window = NULL;
839 ModestMsgViewWindowPrivate *priv = NULL;
840 TnyFolder *header_folder = NULL;
841 ModestHeaderView *header_view = NULL;
842 ModestWindow *main_window = NULL;
843 ModestWindowMgr *mgr = NULL;
845 mgr = modest_runtime_get_window_mgr ();
846 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
847 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
849 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
851 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
853 /* Remember the message list's TreeModel so we can detect changes
854 * and change the list selection when necessary: */
856 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
858 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
859 MODEST_MAIN_WINDOW(main_window),
860 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
863 if (header_view != NULL){
864 header_folder = modest_header_view_get_folder(header_view);
865 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
866 g_assert(header_folder != NULL);
867 priv->header_folder_id = tny_folder_get_id(header_folder);
868 g_assert(priv->header_folder_id != NULL);
869 g_object_unref(header_folder);
872 priv->header_model = g_object_ref(model);
874 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
875 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
876 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
878 priv->row_reference = NULL;
879 priv->next_row_reference = NULL;
882 priv->row_changed_handler = g_signal_connect(
883 GTK_TREE_MODEL(model), "row-changed",
884 G_CALLBACK(modest_msg_view_window_on_row_changed),
886 priv->row_deleted_handler = g_signal_connect(
887 GTK_TREE_MODEL(model), "row-deleted",
888 G_CALLBACK(modest_msg_view_window_on_row_deleted),
890 priv->row_inserted_handler = g_signal_connect (
891 GTK_TREE_MODEL(model), "row-inserted",
892 G_CALLBACK(modest_msg_view_window_on_row_inserted),
894 priv->rows_reordered_handler = g_signal_connect(
895 GTK_TREE_MODEL(model), "rows-reordered",
896 G_CALLBACK(modest_msg_view_window_on_row_reordered),
899 if (header_view != NULL){
900 modest_header_view_add_observer(header_view,
901 MODEST_HEADER_VIEW_OBSERVER(window));
904 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
905 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
906 gtk_widget_show_all (GTK_WIDGET (window));
907 modest_msg_view_window_update_priority (window);
909 /* Check dimming rules */
910 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
911 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
912 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
914 return MODEST_WINDOW(window);
918 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
919 const gchar *modest_account_name,
920 const gchar *msg_uid)
922 ModestMsgViewWindow *window = NULL;
923 ModestMsgViewWindowPrivate *priv = NULL;
924 ModestWindowMgr *mgr = NULL;
926 mgr = modest_runtime_get_window_mgr ();
927 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
928 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
929 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
931 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
933 /* Remember that this is a search result,
934 * so we can disable some UI appropriately: */
935 priv->is_search_result = TRUE;
937 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
939 update_window_title (window);
940 modest_msg_view_window_update_priority (window);
943 return MODEST_WINDOW(window);
947 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
948 const gchar *modest_account_name,
949 const gchar *msg_uid)
952 ModestMsgViewWindowPrivate *priv;
953 ModestWindowMgr *mgr = NULL;
955 g_return_val_if_fail (msg, NULL);
956 mgr = modest_runtime_get_window_mgr ();
957 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
958 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
959 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
960 modest_account_name, msg_uid);
962 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
963 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
965 /* Check dimming rules */
966 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
967 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
968 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
970 return MODEST_WINDOW(obj);
973 void modest_msg_view_window_on_row_changed(
974 GtkTreeModel *header_model,
977 ModestMsgViewWindow *window){
978 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
981 void modest_msg_view_window_on_row_deleted(
982 GtkTreeModel *header_model,
984 ModestMsgViewWindow *window){
985 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
988 /* On insertions we check if the folder still has the message we are
989 * showing or do not. If do not, we do nothing. Which means we are still
990 * not attached to any header folder and thus next/prev buttons are
991 * still dimmed. Once the message that is shown by msg-view is found, the
992 * new model of header-view will be attached and the references will be set.
993 * On each further insertions dimming rules will be checked. However
994 * this requires extra CPU time at least works.
995 * (An message might be deleted from TnyFolder and thus will not be
996 * inserted into the model again for example if it is removed by the
997 * imap server and the header view is refreshed.)
999 void modest_msg_view_window_on_row_inserted(
1000 GtkTreeModel *new_model,
1001 GtkTreePath *tree_path,
1002 GtkTreeIter *tree_iter,
1003 ModestMsgViewWindow *window){
1004 ModestMsgViewWindowPrivate *priv = NULL;
1005 TnyHeader *header = NULL;
1008 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1010 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1012 /* If we already has a model attached then the message shown by
1013 * msg-view is in it, and thus we do not need any actions but
1014 * to check the dimming rules.*/
1015 if(priv->header_model != NULL){
1016 if (priv->row_reference) {
1017 gtk_tree_row_reference_free(priv->next_row_reference);
1018 priv->next_row_reference = gtk_tree_row_reference_copy(
1019 priv->row_reference);
1020 select_next_valid_row (priv->header_model,
1021 &(priv->next_row_reference), FALSE);
1023 modest_ui_actions_check_toolbar_dimming_rules (
1024 MODEST_WINDOW (window));
1028 /* Check if the newly inserted message is the same we are actually
1029 * showing. IF not, we should remain detached from the header model
1030 * and thus prev and next toolbarbuttons should remain dimmed. */
1031 gtk_tree_model_get (new_model, tree_iter,
1032 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1033 uid = modest_tny_folder_get_header_unique_id(header);
1034 if(!g_str_equal(priv->msg_uid, uid)){
1036 g_object_unref(G_OBJECT(header));
1040 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1041 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1042 g_object_unref(G_OBJECT(header));
1046 /* Setup row_reference for the actual msg. */
1047 priv->row_reference = gtk_tree_row_reference_new(
1048 new_model, tree_path);
1049 if(priv->row_reference == NULL){
1050 g_warning("No reference for msg header item.");
1054 /* Attach new_model and connect some callback to it to become able
1055 * to detect changes in header-view. */
1056 priv->header_model = g_object_ref(new_model);
1057 g_signal_connect (new_model, "row-changed",
1058 G_CALLBACK (modest_msg_view_window_on_row_changed),
1060 g_signal_connect (new_model, "row-deleted",
1061 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1063 g_signal_connect (new_model, "rows-reordered",
1064 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1067 /* Now set up next_row_reference. */
1068 priv->next_row_reference = gtk_tree_row_reference_copy(
1069 priv->row_reference);
1070 select_next_valid_row (priv->header_model,
1071 &(priv->next_row_reference), FALSE);
1073 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1076 void modest_msg_view_window_on_row_reordered(
1077 GtkTreeModel *header_model,
1081 ModestMsgViewWindow *window){
1082 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1085 /* The modest_msg_view_window_update_model_replaced implements update
1086 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1087 * actually belongs to the header-view is the same as the TnyFolder of
1088 * the message of msg-view or not. If they are different, there is
1089 * nothing to do. If they are the same, then the model has replaced and
1090 * the reference in msg-view shall be replaced from the old model to
1091 * the new model. In this case the view will be detached from it's
1092 * header folder. From this point the next/prev buttons are dimmed.
1094 void modest_msg_view_window_update_model_replaced(
1095 ModestHeaderViewObserver *observer,
1096 GtkTreeModel *model,
1097 const gchar *tny_folder_id){
1098 ModestMsgViewWindowPrivate *priv = NULL;
1099 ModestMsgViewWindow *window = NULL;
1101 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1102 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1104 window = MODEST_MSG_VIEW_WINDOW(observer);
1105 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1107 /* If there is an other folder in the header-view then we do
1108 * not care about it's model (msg list). Else if the
1109 * header-view shows the folder the msg shown by us is in, we
1110 * shall replace our model reference and make some check. */
1111 if(tny_folder_id == NULL || !g_str_equal(tny_folder_id, priv->header_folder_id))
1114 /* Model is changed(replaced), so we should forget the old
1115 * one. Because there might be other references and there
1116 * might be some change on the model even if we unreferenced
1117 * it, we need to disconnect our signals here. */
1118 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1119 priv->row_changed_handler))
1120 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1121 priv->row_changed_handler);
1122 priv->row_changed_handler = 0;
1123 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1124 priv->row_deleted_handler))
1125 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1126 priv->row_deleted_handler);
1127 priv->row_deleted_handler = 0;
1128 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1129 priv->row_inserted_handler))
1130 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1131 priv->row_inserted_handler);
1132 priv->row_inserted_handler = 0;
1133 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1134 priv->rows_reordered_handler))
1135 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1136 priv->rows_reordered_handler);
1137 priv->rows_reordered_handler = 0;
1138 g_object_unref(priv->header_model);
1139 priv->header_model = NULL;
1140 if (priv->row_reference)
1141 g_object_unref(priv->row_reference);
1142 priv->row_reference = NULL;
1143 if (priv->next_row_reference)
1144 g_object_unref(priv->next_row_reference);
1145 priv->next_row_reference = NULL;
1147 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1149 g_assert(model != NULL);
1151 /* Also we must connect to the new model for row insertions.
1152 * Only for insertions now. We will need other ones only after
1153 * the msg is show by msg-view is added to the new model. */
1154 priv->row_inserted_handler = g_signal_connect (
1155 model, "row-inserted",
1156 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1161 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1163 ModestMsgViewWindowPrivate *priv= NULL;
1165 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1166 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1168 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1172 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1174 ModestMsgViewWindowPrivate *priv= NULL;
1176 TnyHeader *header = NULL;
1177 GtkTreePath *path = NULL;
1180 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1181 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1183 /* If the message was not obtained from a treemodel,
1184 * for instance if it was opened directly by the search UI:
1186 if (priv->header_model == NULL || priv->row_reference == NULL) {
1187 msg = modest_msg_view_window_get_message (self);
1189 header = tny_msg_get_header (msg);
1190 g_object_unref (msg);
1195 /* Get iter of the currently selected message in the header view: */
1196 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1197 * instead of sometimes retrieving it from the header view?
1198 * Then we wouldn't be dependent on the message actually still being selected
1199 * in the header view. murrayc. */
1200 if (!gtk_tree_row_reference_valid (priv->row_reference))
1202 path = gtk_tree_row_reference_get_path (priv->row_reference);
1203 g_return_val_if_fail (path != NULL, NULL);
1204 gtk_tree_model_get_iter (priv->header_model,
1208 /* Get current message header */
1209 gtk_tree_model_get (priv->header_model, &iter,
1210 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1213 gtk_tree_path_free (path);
1218 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1220 ModestMsgViewWindowPrivate *priv;
1222 g_return_val_if_fail (self, NULL);
1224 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1226 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1230 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1232 ModestMsgViewWindowPrivate *priv;
1234 g_return_val_if_fail (self, NULL);
1236 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1238 return (const gchar*) priv->msg_uid;
1242 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1245 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1246 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1247 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1251 is_active = gtk_toggle_action_get_active (toggle);
1254 gtk_widget_show (priv->find_toolbar);
1255 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1257 gtk_widget_hide (priv->find_toolbar);
1260 /* update the toggle buttons status */
1261 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1262 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1263 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1264 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1269 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1270 ModestMsgViewWindow *obj)
1272 GtkToggleAction *toggle;
1273 ModestWindowPrivate *parent_priv;
1274 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1276 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1277 gtk_toggle_action_set_active (toggle, FALSE);
1281 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1282 ModestMsgViewWindow *obj)
1284 gchar *current_search;
1285 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1287 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1288 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1292 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1294 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1295 g_free (current_search);
1296 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1300 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1302 g_free (priv->last_search);
1303 priv->last_search = g_strdup (current_search);
1304 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1307 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1308 g_free (priv->last_search);
1309 priv->last_search = NULL;
1311 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1314 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1315 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1316 g_free (priv->last_search);
1317 priv->last_search = NULL;
1319 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1323 g_free (current_search);
1328 modest_msg_view_window_set_zoom (ModestWindow *window,
1331 ModestMsgViewWindowPrivate *priv;
1332 ModestWindowPrivate *parent_priv;
1333 GtkAction *action = NULL;
1334 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1336 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1338 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1339 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1340 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1342 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1343 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1345 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1349 modest_msg_view_window_get_zoom (ModestWindow *window)
1351 ModestMsgViewWindowPrivate *priv;
1353 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1355 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1356 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1360 modest_msg_view_window_zoom_plus (ModestWindow *window)
1362 ModestWindowPrivate *parent_priv;
1363 GtkRadioAction *zoom_radio_action;
1364 GSList *group, *node;
1366 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1367 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1368 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1370 group = gtk_radio_action_get_group (zoom_radio_action);
1372 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1373 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1377 for (node = group; node != NULL; node = g_slist_next (node)) {
1378 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1379 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1387 modest_msg_view_window_zoom_minus (ModestWindow *window)
1389 ModestWindowPrivate *parent_priv;
1390 GtkRadioAction *zoom_radio_action;
1391 GSList *group, *node;
1393 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1394 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1395 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1397 group = gtk_radio_action_get_group (zoom_radio_action);
1399 for (node = group; node != NULL; node = g_slist_next (node)) {
1400 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1401 if (node->next != NULL) {
1402 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1405 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1415 modest_msg_view_window_key_event (GtkWidget *window,
1421 focus = gtk_window_get_focus (GTK_WINDOW (window));
1423 /* for the find toolbar case */
1424 if (focus && GTK_IS_ENTRY (focus)) {
1425 if (event->keyval == GDK_BackSpace) {
1427 copy = gdk_event_copy ((GdkEvent *) event);
1428 gtk_widget_event (focus, copy);
1429 gdk_event_free (copy);
1434 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1435 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1436 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1437 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1438 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1439 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1440 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1441 gboolean return_value;
1443 if (event->type == GDK_KEY_RELEASE) {
1444 GtkScrollType scroll_type;
1446 switch (event->keyval) {
1449 scroll_type = GTK_SCROLL_STEP_UP; break;
1452 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1454 case GDK_KP_Page_Up:
1455 scroll_type = GTK_SCROLL_PAGE_UP; break;
1457 case GDK_KP_Page_Down:
1458 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1461 scroll_type = GTK_SCROLL_START; break;
1464 scroll_type = GTK_SCROLL_END; break;
1465 default: scroll_type = GTK_SCROLL_NONE;
1468 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",
1469 scroll_type, FALSE, &return_value);
1480 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1483 ModestMsgViewWindowPrivate *priv;
1484 GtkTreeIter tmp_iter;
1485 gboolean is_last_selected;
1487 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1490 /*if no model (so no rows at all), then virtually we are the last*/
1491 if (!priv->header_model || !priv->row_reference)
1494 path = gtk_tree_row_reference_get_path (priv->row_reference);
1498 is_last_selected = TRUE;
1499 while (is_last_selected) {
1501 gtk_tree_path_next (path);
1502 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1504 gtk_tree_model_get (priv->header_model, &tmp_iter,
1505 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1508 if (msg_is_visible (header, priv->is_outbox))
1509 is_last_selected = FALSE;
1510 g_object_unref(G_OBJECT(header));
1513 gtk_tree_path_free (path);
1514 return is_last_selected;
1518 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1520 ModestMsgViewWindowPrivate *priv;
1522 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1523 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1525 return priv->header_model != NULL;
1529 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1531 ModestMsgViewWindowPrivate *priv;
1533 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1534 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1536 return priv->is_search_result;
1540 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1542 return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1543 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1548 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1551 ModestMsgViewWindowPrivate *priv;
1552 gboolean is_first_selected;
1553 GtkTreeIter tmp_iter;
1554 /* gchar * path_string;*/
1556 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1557 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1559 /*if no model (so no rows at all), then virtually we are the first*/
1560 if (!priv->header_model || !priv->row_reference)
1563 path = gtk_tree_row_reference_get_path (priv->row_reference);
1567 /* path_string = gtk_tree_path_to_string (path);
1568 is_first_selected = strcmp (path_string, "0");
1570 g_free (path_string);
1571 gtk_tree_path_free (path);
1573 return is_first_selected;*/
1575 is_first_selected = TRUE;
1576 while (is_first_selected) {
1578 if(!gtk_tree_path_prev (path))
1580 /* Here the 'if' is needless for logic, but let make sure
1581 * iter is valid for gtk_tree_model_get. */
1582 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1584 gtk_tree_model_get (priv->header_model, &tmp_iter,
1585 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1588 if (msg_is_visible (header, priv->is_outbox))
1589 is_first_selected = FALSE;
1590 g_object_unref(G_OBJECT(header));
1593 gtk_tree_path_free (path);
1594 return is_first_selected;
1598 * Reads the message whose summary item is @header. It takes care of
1599 * several things, among others:
1601 * If the message was not previously downloaded then ask the user
1602 * before downloading. If there is no connection launch the connection
1603 * dialog. Update toolbar dimming rules.
1605 * Returns: TRUE if the mail operation was started, otherwise if the
1606 * user do not want to download the message, or if the user do not
1607 * want to connect, then the operation is not issued
1610 message_reader (ModestMsgViewWindow *window,
1611 ModestMsgViewWindowPrivate *priv,
1613 GtkTreeRowReference *row_reference)
1615 ModestMailOperation *mail_op = NULL;
1616 gboolean already_showing = FALSE;
1617 ModestWindow *msg_window = NULL;
1618 ModestWindowMgr *mgr;
1620 g_return_val_if_fail (row_reference != NULL, FALSE);
1622 mgr = modest_runtime_get_window_mgr ();
1623 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1624 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1627 gtk_window_present (GTK_WINDOW (msg_window));
1628 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1632 /* Msg download completed */
1633 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1634 /* Ask the user if he wants to download the message if
1636 if (!tny_device_is_online (modest_runtime_get_device())) {
1637 TnyFolder *folder = NULL;
1638 GtkResponseType response;
1640 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1641 _("mcen_nc_get_msg"));
1642 if (response == GTK_RESPONSE_CANCEL)
1645 /* Offer the connection dialog if necessary */
1646 folder = tny_header_get_folder (header);
1648 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1649 TNY_FOLDER_STORE (folder))) {
1650 g_object_unref (folder);
1653 g_object_unref (folder);
1658 /* New mail operation */
1659 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(window),
1660 modest_ui_actions_get_msgs_full_error_handler,
1663 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1664 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, row_reference);
1665 g_object_unref (mail_op);
1667 /* Update dimming rules */
1668 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1669 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1675 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1677 ModestMsgViewWindowPrivate *priv;
1678 GtkTreePath *path= NULL;
1679 GtkTreeIter tmp_iter;
1681 gboolean retval = TRUE;
1682 GtkTreeRowReference *row_reference = NULL;
1684 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1685 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1687 if (!priv->row_reference)
1690 /* Update the next row reference if it's not valid. This could
1691 happen if for example the header which it was pointing to,
1692 was deleted. The best place to do it is in the row-deleted
1693 handler but the tinymail model do not work like the glib
1694 tree models and reports the deletion when the row is still
1696 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1697 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1698 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1699 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1702 if (priv->next_row_reference)
1703 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1707 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1709 gtk_tree_model_get_iter (priv->header_model,
1712 gtk_tree_path_free (path);
1714 gtk_tree_model_get (priv->header_model, &tmp_iter,
1715 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1718 /* Read the message & show it */
1719 if (!message_reader (window, priv, header, row_reference)) {
1721 gtk_tree_row_reference_free (row_reference);
1725 g_object_unref (header);
1731 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1733 ModestMsgViewWindowPrivate *priv = NULL;
1735 gboolean finished = FALSE;
1736 gboolean retval = FALSE;
1738 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1739 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1741 /* Return inmediatly if there is no header model */
1742 if (!priv->header_model || !priv->row_reference)
1745 path = gtk_tree_row_reference_get_path (priv->row_reference);
1746 while (!finished && gtk_tree_path_prev (path)) {
1750 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1751 gtk_tree_model_get (priv->header_model, &iter,
1752 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1756 if (msg_is_visible (header, priv->is_outbox)) {
1757 GtkTreeRowReference *row_reference;
1758 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1759 /* Read the message & show it */
1760 retval = message_reader (window, priv, header, row_reference);
1762 gtk_tree_row_reference_free (row_reference);
1766 g_object_unref (header);
1770 gtk_tree_path_free (path);
1775 view_msg_cb (ModestMailOperation *mail_op,
1782 ModestMsgViewWindow *self = NULL;
1783 ModestMsgViewWindowPrivate *priv = NULL;
1784 GtkTreeRowReference *row_reference = NULL;
1787 g_object_unref (self);
1791 /* If there was any error */
1792 row_reference = (GtkTreeRowReference *) user_data;
1793 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1794 gtk_tree_row_reference_free (row_reference);
1798 /* Get the window */
1799 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1800 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1801 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1803 /* Update the row reference */
1804 if (priv->row_reference != NULL) {
1805 gtk_tree_row_reference_free (priv->row_reference);
1806 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1807 if (priv->next_row_reference != NULL) {
1808 gtk_tree_row_reference_free (priv->next_row_reference);
1810 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1811 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1812 gtk_tree_row_reference_free (row_reference);
1815 /* Mark header as read */
1816 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1817 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1819 /* Set new message */
1820 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1821 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1822 modest_msg_view_window_update_priority (self);
1823 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1824 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1827 /* Set the new message uid of the window */
1828 if (priv->msg_uid) {
1829 g_free (priv->msg_uid);
1830 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1833 /* Notify the observers */
1834 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1835 0, priv->header_model, priv->row_reference);
1837 /* Free new references */
1838 g_object_unref (self);
1842 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1844 ModestMsgViewWindowPrivate *priv;
1846 TnyFolderType folder_type;
1848 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1850 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1852 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1856 folder = tny_msg_get_folder (msg);
1858 folder_type = tny_folder_get_folder_type (folder);
1859 g_object_unref (folder);
1861 g_object_unref (msg);
1869 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1871 ModestMsgViewWindowPrivate *priv;
1872 TnyHeader *header = NULL;
1873 TnyHeaderFlags flags = 0;
1875 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1877 if (priv->header_model && priv->row_reference) {
1879 GtkTreePath *path = NULL;
1881 path = gtk_tree_row_reference_get_path (priv->row_reference);
1882 g_return_if_fail (path != NULL);
1883 gtk_tree_model_get_iter (priv->header_model,
1885 gtk_tree_row_reference_get_path (priv->row_reference));
1887 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1889 gtk_tree_path_free (path);
1892 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1894 header = tny_msg_get_header (msg);
1895 g_object_unref (msg);
1900 flags = tny_header_get_flags (header);
1901 g_object_unref(G_OBJECT(header));
1904 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1909 toolbar_resize (ModestMsgViewWindow *self)
1911 ModestMsgViewWindowPrivate *priv = NULL;
1912 ModestWindowPrivate *parent_priv = NULL;
1914 gint static_button_size;
1915 ModestWindowMgr *mgr;
1917 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1918 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1919 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1921 mgr = modest_runtime_get_window_mgr ();
1922 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
1924 if (parent_priv->toolbar) {
1925 /* left size buttons */
1926 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
1927 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1928 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1929 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1930 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
1931 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1932 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1933 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1934 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
1935 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1936 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1937 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1938 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1939 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1940 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1941 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1943 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
1944 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1945 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1946 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1947 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1948 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1949 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1950 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1956 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1958 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1959 ModestWindowPrivate *parent_priv;
1960 ModestWindowMgr *mgr;
1961 gboolean is_fullscreen;
1962 GtkAction *fs_toggle_action;
1965 mgr = modest_runtime_get_window_mgr ();
1966 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1968 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1970 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1971 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1972 if (is_fullscreen != active) {
1973 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1975 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
1983 modest_msg_view_window_show_toolbar (ModestWindow *self,
1984 gboolean show_toolbar)
1986 ModestMsgViewWindowPrivate *priv = NULL;
1987 ModestWindowPrivate *parent_priv;
1988 GtkWidget *reply_button = NULL, *menu = NULL;
1989 GtkWidget *placeholder = NULL;
1991 const gchar *action_name;
1994 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1995 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1997 /* Set optimized view status */
1998 priv->optimized_view = !show_toolbar;
2000 if (!parent_priv->toolbar) {
2001 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2003 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2005 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2006 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2007 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2008 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2009 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2011 /* Add ProgressBar (Transfer toolbar) */
2012 priv->progress_bar = modest_progress_bar_widget_new ();
2013 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2014 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2015 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2016 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2017 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2019 /* Connect cancel 'clicked' signal to abort progress mode */
2020 g_signal_connect(priv->cancel_toolitem, "clicked",
2021 G_CALLBACK(cancel_progressbar),
2024 /* Add it to the observers list */
2025 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2028 hildon_window_add_toolbar (HILDON_WINDOW (self),
2029 GTK_TOOLBAR (parent_priv->toolbar));
2031 /* Set reply button tap and hold menu */
2032 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2033 "/ToolBar/ToolbarMessageReply");
2034 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2035 "/ToolbarReplyCSM");
2036 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2040 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2041 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2042 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2044 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2045 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2046 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2048 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2051 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2052 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2055 /* Update also the actions (to update the toggles in the
2056 menus), we have to do it manually because some other window
2057 of the same time could have changed it (remember that the
2058 toolbar fullscreen mode is shared by all the windows of the
2060 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2061 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2063 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2065 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2066 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2071 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2073 ModestMsgViewWindow *window)
2075 if (!GTK_WIDGET_VISIBLE (window))
2078 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2082 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2084 ModestMsgViewWindowPrivate *priv;
2086 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2087 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2089 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2093 cancel_progressbar (GtkToolButton *toolbutton,
2094 ModestMsgViewWindow *self)
2097 ModestMsgViewWindowPrivate *priv;
2099 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2101 /* Get operation observers and cancel its current operation */
2102 tmp = priv->progress_widgets;
2104 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2105 tmp=g_slist_next(tmp);
2109 observers_empty (ModestMsgViewWindow *self)
2112 ModestMsgViewWindowPrivate *priv;
2113 gboolean is_empty = TRUE;
2114 guint pending_ops = 0;
2116 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2117 tmp = priv->progress_widgets;
2119 /* Check all observers */
2120 while (tmp && is_empty) {
2121 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2122 is_empty = pending_ops == 0;
2124 tmp = g_slist_next(tmp);
2131 on_account_removed (TnyAccountStore *account_store,
2132 TnyAccount *account,
2135 /* Do nothing if it's a transport account, because we only
2136 show the messages of a store account */
2137 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2138 const gchar *parent_acc = NULL;
2139 const gchar *our_acc = NULL;
2141 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2142 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2144 /* Close this window if I'm showing a message of the removed account */
2145 if (strcmp (parent_acc, our_acc) == 0)
2146 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2151 on_mail_operation_started (ModestMailOperation *mail_op,
2154 ModestMsgViewWindow *self;
2155 ModestMailOperationTypeOperation op_type;
2157 ModestMsgViewWindowPrivate *priv;
2158 GObject *source = NULL;
2160 self = MODEST_MSG_VIEW_WINDOW (user_data);
2161 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2162 op_type = modest_mail_operation_get_type_operation (mail_op);
2163 tmp = priv->progress_widgets;
2164 source = modest_mail_operation_get_source(mail_op);
2165 if (G_OBJECT (self) == source) {
2166 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2167 set_toolbar_transfer_mode(self);
2169 modest_progress_object_add_operation (
2170 MODEST_PROGRESS_OBJECT (tmp->data),
2172 tmp = g_slist_next (tmp);
2176 g_object_unref (source);
2180 on_mail_operation_finished (ModestMailOperation *mail_op,
2183 ModestMsgViewWindow *self;
2184 ModestMailOperationTypeOperation op_type;
2186 ModestMsgViewWindowPrivate *priv;
2188 self = MODEST_MSG_VIEW_WINDOW (user_data);
2189 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2190 op_type = modest_mail_operation_get_type_operation (mail_op);
2191 tmp = priv->progress_widgets;
2193 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2195 modest_progress_object_remove_operation (
2196 MODEST_PROGRESS_OBJECT (tmp->data),
2198 tmp = g_slist_next (tmp);
2201 /* If no more operations are being observed, NORMAL mode is enabled again */
2202 if (observers_empty (self)) {
2203 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2209 on_queue_changed (ModestMailOperationQueue *queue,
2210 ModestMailOperation *mail_op,
2211 ModestMailOperationQueueNotification type,
2212 ModestMsgViewWindow *self)
2214 ModestMsgViewWindowPrivate *priv;
2216 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2218 /* If this operations was created by another window, do nothing */
2219 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2222 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2223 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2225 "operation-started",
2226 G_CALLBACK (on_mail_operation_started),
2228 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2230 "operation-finished",
2231 G_CALLBACK (on_mail_operation_finished),
2233 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2234 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2236 "operation-started");
2237 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2239 "operation-finished");
2244 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2246 ModestMsgViewWindowPrivate *priv;
2247 TnyList *selected_attachments = NULL;
2249 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2250 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2252 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2254 return selected_attachments;
2258 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2260 ModestMsgViewWindowPrivate *priv;
2261 const gchar *msg_uid;
2262 gchar *attachment_uid = NULL;
2263 gint attachment_index = 0;
2264 TnyList *attachments;
2266 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2267 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2268 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2270 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2271 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2272 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2273 g_object_unref (attachments);
2275 if (msg_uid && attachment_index >= 0) {
2276 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2279 if (mime_part == NULL) {
2280 gboolean error = FALSE;
2281 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2282 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2284 } else if (tny_list_get_length (selected_attachments) > 1) {
2285 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2289 iter = tny_list_create_iterator (selected_attachments);
2290 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2291 g_object_unref (iter);
2293 g_object_unref (selected_attachments);
2298 g_object_ref (mime_part);
2301 if (tny_mime_part_is_purged (mime_part)) {
2302 g_object_unref (mime_part);
2306 if (!TNY_IS_MSG (mime_part)) {
2307 gchar *filepath = NULL;
2308 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2309 const gchar *content_type;
2310 gboolean show_error_banner = FALSE;
2312 TnyFsStream *temp_stream = NULL;
2313 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2316 if (temp_stream != NULL) {
2317 content_type = tny_mime_part_get_content_type (mime_part);
2318 if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2319 /* make the file read-only */
2320 if (g_chmod(filepath, 0444) != 0)
2321 g_warning ("%s: failed to set file '%s' to read-only: %s",
2322 __FUNCTION__, filepath, strerror(errno));
2324 modest_platform_activate_file (filepath, content_type);
2326 /* error while saving attachment, maybe cerm_device_memory_full */
2327 show_error_banner = TRUE;
2329 g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2333 g_object_unref (temp_stream);
2335 /* NOTE: files in the temporary area will be automatically
2336 * cleaned after some time if they are no longer in use */
2338 if (filepath != NULL) {
2339 /* the file may already exist but it isn't writable,
2340 * let's try to open it anyway */
2341 content_type = tny_mime_part_get_content_type (mime_part);
2342 modest_platform_activate_file (filepath, content_type);
2345 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2346 show_error_banner = TRUE;
2349 if (show_error_banner)
2350 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2352 /* message attachment */
2353 TnyHeader *header = NULL;
2354 ModestWindowMgr *mgr;
2355 ModestWindow *msg_win = NULL;
2358 header = tny_msg_get_header (TNY_MSG (mime_part));
2359 mgr = modest_runtime_get_window_mgr ();
2360 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2363 if (msg_win) /* there is already a window for this uid; top it */
2364 gtk_window_present (GTK_WINDOW(msg_win));
2366 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2367 * thus, we don't do anything */
2368 g_warning ("window for is already being created");
2370 /* it's not found, so create a new window for it */
2371 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2372 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2374 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2375 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2376 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2377 modest_window_get_zoom (MODEST_WINDOW (window)));
2378 modest_window_mgr_register_window (mgr, msg_win);
2379 gtk_widget_show_all (GTK_WIDGET (msg_win));
2382 g_object_unref (mime_part);
2398 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2399 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2400 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2401 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2404 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2408 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2409 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2410 g_free (pair->filename);
2411 g_object_unref (pair->part);
2412 g_slice_free (SaveMimePartPair, pair);
2414 g_list_free (info->pairs);
2417 gtk_widget_destroy (info->banner);
2418 g_object_unref (info->banner);
2419 g_slice_free (SaveMimePartInfo, info);
2424 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2426 if (info->pairs != NULL) {
2427 save_mime_part_to_file (info);
2430 result = info->result;
2432 /* This is a GDK lock because we are an idle callback and
2433 * hildon_banner_show_information is or does Gtk+ code */
2435 gdk_threads_enter (); /* CHECKED */
2436 save_mime_part_info_free (info, TRUE);
2438 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2440 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2442 gdk_threads_leave (); /* CHECKED */
2449 save_mime_part_to_file (SaveMimePartInfo *info)
2451 GnomeVFSResult result;
2452 GnomeVFSHandle *handle;
2454 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2455 gboolean decode_result = TRUE;
2457 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2458 if (result == GNOME_VFS_OK) {
2459 stream = tny_vfs_stream_new (handle);
2460 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2461 decode_result = FALSE;
2463 g_object_unref (G_OBJECT (stream));
2464 g_object_unref (pair->part);
2465 g_slice_free (SaveMimePartPair, pair);
2466 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2467 info->result = decode_result;
2469 save_mime_part_info_free (info, FALSE);
2470 info->result = FALSE;
2473 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2478 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2480 gboolean is_ok = TRUE;
2481 gint replaced_files = 0;
2482 const GList *files = info->pairs;
2485 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2486 SaveMimePartPair *pair = iter->data;
2487 if (modest_utils_file_exists (pair->filename)) {
2491 if (replaced_files) {
2492 GtkWidget *confirm_overwrite_dialog;
2493 const gchar *message = (replaced_files == 1) ?
2494 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2495 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2496 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2499 gtk_widget_destroy (confirm_overwrite_dialog);
2503 save_mime_part_info_free (info, TRUE);
2505 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2506 _CS("sfil_ib_saving"));
2507 info->banner = g_object_ref (banner);
2508 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2509 g_object_unref (banner);
2516 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2518 ModestMsgViewWindowPrivate *priv;
2519 GList *files_to_save = NULL;
2520 GtkWidget *save_dialog = NULL;
2521 gchar *folder = NULL;
2522 gboolean canceled = FALSE;
2523 const gchar *filename = NULL;
2524 gchar *save_multiple_str = NULL;
2526 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2527 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2529 if (mime_parts == NULL) {
2530 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2531 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2534 g_object_ref (mime_parts);
2537 /* prepare dialog */
2538 if (tny_list_get_length (mime_parts) == 1) {
2540 /* only one attachment selected */
2541 iter = tny_list_create_iterator (mime_parts);
2542 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2543 g_object_unref (iter);
2544 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2545 filename = tny_mime_part_get_filename (mime_part);
2547 g_warning ("Tried to save a non-file attachment");
2550 g_object_unref (mime_part);
2552 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2553 tny_list_get_length (mime_parts));
2556 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2557 GTK_FILE_CHOOSER_ACTION_SAVE);
2560 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2561 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2565 if (filename != NULL)
2566 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2569 /* if multiple, set multiple string */
2570 if (save_multiple_str) {
2571 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2575 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2576 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2578 if (!modest_utils_folder_writable (chooser_uri)) {
2579 hildon_banner_show_information
2580 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2584 iter = tny_list_create_iterator (mime_parts);
2585 while (!tny_iterator_is_done (iter)) {
2586 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2588 tny_iterator_next (iter);
2589 if (tny_mime_part_is_attachment (mime_part)) {
2590 SaveMimePartPair *pair;
2592 if ((!tny_iterator_is_done (iter)) &&
2593 (tny_mime_part_get_filename (mime_part) == NULL)) {
2594 g_object_unref (mime_part);
2598 pair = g_slice_new0 (SaveMimePartPair);
2599 if (tny_iterator_is_done (iter)) {
2600 pair->filename = g_strdup (chooser_uri);
2603 g_build_filename (chooser_uri,
2604 tny_mime_part_get_filename (mime_part), NULL);
2606 pair->part = mime_part;
2607 files_to_save = g_list_prepend (files_to_save, pair);
2610 g_object_unref (iter);
2612 g_free (chooser_uri);
2615 gtk_widget_destroy (save_dialog);
2617 g_object_unref (mime_parts);
2619 if (files_to_save != NULL) {
2620 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2621 info->pairs = files_to_save;
2622 info->result = TRUE;
2623 save_mime_parts_to_file_with_checks (info);
2628 show_remove_attachment_information (gpointer userdata)
2630 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2631 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2633 /* We're outside the main lock */
2634 gdk_threads_enter ();
2636 if (priv->remove_attachment_banner != NULL) {
2637 gtk_widget_destroy (priv->remove_attachment_banner);
2638 g_object_unref (priv->remove_attachment_banner);
2641 priv->remove_attachment_banner = g_object_ref (
2642 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2644 gdk_threads_leave ();
2650 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2652 ModestMsgViewWindowPrivate *priv;
2653 TnyList *mime_parts = NULL;
2654 gchar *confirmation_message;
2659 /* TnyFolder *folder; */
2661 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2662 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2665 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2667 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2669 /* Remove already purged messages from mime parts list */
2670 iter = tny_list_create_iterator (mime_parts);
2671 while (!tny_iterator_is_done (iter)) {
2672 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2673 tny_iterator_next (iter);
2674 if (tny_mime_part_is_purged (part)) {
2675 tny_list_remove (mime_parts, (GObject *) part);
2677 g_object_unref (part);
2679 g_object_unref (iter);
2681 if (tny_list_get_length (mime_parts) == 0) {
2682 g_object_unref (mime_parts);
2686 n_attachments = tny_list_get_length (mime_parts);
2687 if (n_attachments == 1) {
2688 const gchar *filename;
2691 iter = tny_list_create_iterator (mime_parts);
2692 part = (TnyMimePart *) tny_iterator_get_current (iter);
2693 g_object_unref (iter);
2694 if (TNY_IS_MSG (part)) {
2696 header = tny_msg_get_header (TNY_MSG (part));
2697 filename = tny_header_get_subject (header);
2698 g_object_unref (header);
2699 if (filename == NULL)
2700 filename = _("mail_va_no_subject");
2702 filename = tny_mime_part_get_filename (TNY_MIME_PART (part));
2704 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2705 g_object_unref (part);
2707 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2708 "mcen_nc_purge_files_text",
2709 n_attachments), n_attachments);
2711 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2712 confirmation_message);
2713 g_free (confirmation_message);
2715 if (response != GTK_RESPONSE_OK) {
2716 g_object_unref (mime_parts);
2720 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2721 /* folder = tny_msg_get_folder (msg); */
2722 /* tny_msg_uncache_attachments (msg); */
2723 /* tny_folder_refresh (folder, NULL); */
2724 /* g_object_unref (folder); */
2726 iter = tny_list_create_iterator (mime_parts);
2727 while (!tny_iterator_is_done (iter)) {
2730 part = (TnyMimePart *) tny_iterator_get_current (iter);
2731 tny_mime_part_set_purged (TNY_MIME_PART (part));
2732 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2733 g_object_unref (part);
2734 tny_iterator_next (iter);
2736 g_object_unref (iter);
2738 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2739 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2740 tny_msg_rewrite_cache (msg);
2741 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2742 g_object_unref (msg);
2744 g_object_unref (mime_parts);
2746 if (priv->purge_timeout > 0) {
2747 g_source_remove (priv->purge_timeout);
2748 priv->purge_timeout = 0;
2751 if (priv->remove_attachment_banner) {
2752 gtk_widget_destroy (priv->remove_attachment_banner);
2753 g_object_unref (priv->remove_attachment_banner);
2754 priv->remove_attachment_banner = NULL;
2762 update_window_title (ModestMsgViewWindow *window)
2764 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2766 TnyHeader *header = NULL;
2767 const gchar *subject = NULL;
2769 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2772 header = tny_msg_get_header (msg);
2773 subject = tny_header_get_subject (header);
2774 g_object_unref (msg);
2777 if ((subject == NULL)||(subject[0] == '\0'))
2778 subject = _("mail_va_no_subject");
2780 gtk_window_set_title (GTK_WINDOW (window), subject);
2784 static void on_move_focus (GtkWidget *widget,
2785 GtkDirectionType direction,
2788 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");