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;
541 ModestWindowPrivate *parent_priv;
543 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
544 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
546 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
547 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
548 main_vbox = gtk_vbox_new (FALSE, 6);
550 #ifdef MODEST_USE_MOZEMBED
551 priv->main_scroll = priv->msg_view;
552 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
554 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
555 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
557 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
558 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
559 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
561 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
562 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
564 priv->find_toolbar = hildon_find_toolbar_new (NULL);
565 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
566 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
568 gtk_widget_show_all (GTK_WIDGET(main_vbox));
572 modest_msg_view_window_disconnect_signals (ModestWindow *self)
574 ModestMsgViewWindowPrivate *priv;
575 ModestHeaderView *header_view = NULL;
576 ModestWindow *main_window = NULL;
578 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
580 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
581 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
582 priv->clipboard_change_handler))
583 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
584 priv->clipboard_change_handler);
586 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
587 priv->queue_change_handler))
588 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
589 priv->queue_change_handler);
591 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
592 priv->account_removed_handler))
593 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
594 priv->account_removed_handler);
596 if (priv->header_model) {
597 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
598 priv->row_changed_handler))
599 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
600 priv->row_changed_handler);
602 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
603 priv->row_deleted_handler))
604 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
605 priv->row_deleted_handler);
607 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
608 priv->row_inserted_handler))
609 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
610 priv->row_inserted_handler);
612 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
613 priv->rows_reordered_handler))
614 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
615 priv->rows_reordered_handler);
618 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
619 priv->sighandlers = NULL;
621 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
622 FALSE); /* don't create */
626 header_view = MODEST_HEADER_VIEW(
627 modest_main_window_get_child_widget(
628 MODEST_MAIN_WINDOW(main_window),
629 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
630 if (header_view == NULL)
633 modest_header_view_remove_observer(header_view,
634 MODEST_HEADER_VIEW_OBSERVER(self));
638 modest_msg_view_window_finalize (GObject *obj)
640 ModestMsgViewWindowPrivate *priv;
642 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
644 /* Sanity check: shouldn't be needed, the window mgr should
645 call this function before */
646 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
648 if (priv->header_model != NULL) {
649 g_object_unref (priv->header_model);
650 priv->header_model = NULL;
653 if (priv->progress_bar_timeout > 0) {
654 g_source_remove (priv->progress_bar_timeout);
655 priv->progress_bar_timeout = 0;
658 if (priv->remove_attachment_banner) {
659 gtk_widget_destroy (priv->remove_attachment_banner);
660 g_object_unref (priv->remove_attachment_banner);
661 priv->remove_attachment_banner = NULL;
664 if (priv->purge_timeout > 0) {
665 g_source_remove (priv->purge_timeout);
666 priv->purge_timeout = 0;
669 if (priv->row_reference) {
670 gtk_tree_row_reference_free (priv->row_reference);
671 priv->row_reference = NULL;
674 if (priv->next_row_reference) {
675 gtk_tree_row_reference_free (priv->next_row_reference);
676 priv->next_row_reference = NULL;
680 g_free (priv->msg_uid);
681 priv->msg_uid = NULL;
684 G_OBJECT_CLASS(parent_class)->finalize (obj);
688 select_next_valid_row (GtkTreeModel *model,
689 GtkTreeRowReference **row_reference,
692 GtkTreeIter tmp_iter;
693 GtkTreePath *path, *next;
694 gboolean retval = FALSE;
696 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
698 path = gtk_tree_row_reference_get_path (*row_reference);
699 gtk_tree_model_get_iter (model, &tmp_iter, path);
700 gtk_tree_row_reference_free (*row_reference);
701 *row_reference = NULL;
703 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
704 next = gtk_tree_model_get_path (model, &tmp_iter);
705 *row_reference = gtk_tree_row_reference_new (model, next);
707 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
708 next = gtk_tree_model_get_path (model, &tmp_iter);
710 /* Ensure that we are not selecting the same */
711 if (gtk_tree_path_compare (path, next) != 0) {
712 *row_reference = gtk_tree_row_reference_new (model, next);
718 gtk_tree_path_free (path);
723 /* TODO: This should be in _init(), with the parameters as properties. */
725 modest_msg_view_window_construct (ModestMsgViewWindow *self,
726 const gchar *modest_account_name,
727 const gchar *msg_uid)
730 ModestMsgViewWindowPrivate *priv = NULL;
731 ModestWindowPrivate *parent_priv = NULL;
732 ModestDimmingRulesGroup *menu_rules_group = NULL;
733 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
734 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
736 obj = G_OBJECT (self);
737 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
738 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
740 priv->msg_uid = g_strdup (msg_uid);
743 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
744 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
745 gtk_widget_show (parent_priv->menubar);
746 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
748 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
749 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
750 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
752 /* Add common dimming rules */
753 modest_dimming_rules_group_add_rules (menu_rules_group,
754 modest_msg_view_menu_dimming_entries,
755 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
756 MODEST_WINDOW (self));
757 modest_dimming_rules_group_add_rules (toolbar_rules_group,
758 modest_msg_view_toolbar_dimming_entries,
759 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
760 MODEST_WINDOW (self));
761 modest_dimming_rules_group_add_rules (clipboard_rules_group,
762 modest_msg_view_clipboard_dimming_entries,
763 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
764 MODEST_WINDOW (self));
766 /* Insert dimming rules group for this window */
767 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
768 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
769 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
770 g_object_unref (menu_rules_group);
771 g_object_unref (toolbar_rules_group);
772 g_object_unref (clipboard_rules_group);
774 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
776 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
778 priv->clipboard_change_handler = g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", G_CALLBACK (modest_msg_view_window_clipboard_owner_change), obj);
779 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
780 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
781 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
782 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
783 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
784 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
785 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
786 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
787 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
788 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
790 g_signal_connect (G_OBJECT (obj), "key-release-event",
791 G_CALLBACK (modest_msg_view_window_key_event),
794 g_signal_connect (G_OBJECT (obj), "key-press-event",
795 G_CALLBACK (modest_msg_view_window_key_event),
798 g_signal_connect (G_OBJECT (obj), "window-state-event",
799 G_CALLBACK (modest_msg_view_window_window_state_event),
802 g_signal_connect (G_OBJECT (obj), "move-focus",
803 G_CALLBACK (on_move_focus), obj);
805 /* Mail Operation Queue */
806 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
808 G_CALLBACK (on_queue_changed),
811 /* Account manager */
812 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
814 G_CALLBACK(on_account_removed),
817 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
819 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
820 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
821 priv->last_search = NULL;
823 /* Init the clipboard actions dim status */
824 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
826 gtk_widget_show_all (GTK_WIDGET (obj));
827 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
832 /* FIXME: parameter checks */
834 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
835 const gchar *modest_account_name,
836 const gchar *msg_uid,
838 GtkTreeRowReference *row_reference)
840 ModestMsgViewWindow *window = NULL;
841 ModestMsgViewWindowPrivate *priv = NULL;
842 TnyFolder *header_folder = NULL;
843 ModestHeaderView *header_view = NULL;
844 ModestWindow *main_window = NULL;
845 ModestWindowMgr *mgr = NULL;
847 mgr = modest_runtime_get_window_mgr ();
848 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
849 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
851 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
853 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
855 /* Remember the message list's TreeModel so we can detect changes
856 * and change the list selection when necessary: */
858 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
860 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
861 MODEST_MAIN_WINDOW(main_window),
862 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
865 if (header_view != NULL){
866 header_folder = modest_header_view_get_folder(header_view);
867 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
868 g_assert(header_folder != NULL);
869 priv->header_folder_id = tny_folder_get_id(header_folder);
870 g_assert(priv->header_folder_id != NULL);
871 g_object_unref(header_folder);
874 priv->header_model = g_object_ref(model);
876 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
877 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
878 select_next_valid_row (model, &(priv->next_row_reference), TRUE);
880 priv->row_reference = NULL;
881 priv->next_row_reference = NULL;
884 priv->row_changed_handler = g_signal_connect(
885 GTK_TREE_MODEL(model), "row-changed",
886 G_CALLBACK(modest_msg_view_window_on_row_changed),
888 priv->row_deleted_handler = g_signal_connect(
889 GTK_TREE_MODEL(model), "row-deleted",
890 G_CALLBACK(modest_msg_view_window_on_row_deleted),
892 priv->row_inserted_handler = g_signal_connect (
893 GTK_TREE_MODEL(model), "row-inserted",
894 G_CALLBACK(modest_msg_view_window_on_row_inserted),
896 priv->rows_reordered_handler = g_signal_connect(
897 GTK_TREE_MODEL(model), "rows-reordered",
898 G_CALLBACK(modest_msg_view_window_on_row_reordered),
901 if (header_view != NULL){
902 modest_header_view_add_observer(header_view,
903 MODEST_HEADER_VIEW_OBSERVER(window));
906 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
907 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
908 gtk_widget_show_all (GTK_WIDGET (window));
909 modest_msg_view_window_update_priority (window);
911 /* Check dimming rules */
912 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
913 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
914 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
916 return MODEST_WINDOW(window);
920 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
921 const gchar *modest_account_name,
922 const gchar *msg_uid)
924 ModestMsgViewWindow *window = NULL;
925 ModestMsgViewWindowPrivate *priv = NULL;
926 ModestWindowMgr *mgr = NULL;
928 mgr = modest_runtime_get_window_mgr ();
929 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
930 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
931 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
933 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
935 /* Remember that this is a search result,
936 * so we can disable some UI appropriately: */
937 priv->is_search_result = TRUE;
939 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
941 update_window_title (window);
942 modest_msg_view_window_update_priority (window);
945 return MODEST_WINDOW(window);
949 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
950 const gchar *modest_account_name,
951 const gchar *msg_uid)
954 ModestMsgViewWindowPrivate *priv;
955 ModestWindowMgr *mgr = NULL;
957 g_return_val_if_fail (msg, NULL);
958 mgr = modest_runtime_get_window_mgr ();
959 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
960 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
961 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
962 modest_account_name, msg_uid);
964 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
965 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
967 /* Check dimming rules */
968 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
969 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
970 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
972 return MODEST_WINDOW(obj);
975 void modest_msg_view_window_on_row_changed(
976 GtkTreeModel *header_model,
979 ModestMsgViewWindow *window){
980 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
983 void modest_msg_view_window_on_row_deleted(
984 GtkTreeModel *header_model,
986 ModestMsgViewWindow *window){
987 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
990 /* On insertions we check if the folder still has the message we are
991 * showing or do not. If do not, we do nothing. Which means we are still
992 * not attached to any header folder and thus next/prev buttons are
993 * still dimmed. Once the message that is shown by msg-view is found, the
994 * new model of header-view will be attached and the references will be set.
995 * On each further insertions dimming rules will be checked. However
996 * this requires extra CPU time at least works.
997 * (An message might be deleted from TnyFolder and thus will not be
998 * inserted into the model again for example if it is removed by the
999 * imap server and the header view is refreshed.)
1001 void modest_msg_view_window_on_row_inserted(
1002 GtkTreeModel *new_model,
1003 GtkTreePath *tree_path,
1004 GtkTreeIter *tree_iter,
1005 ModestMsgViewWindow *window){
1006 ModestMsgViewWindowPrivate *priv = NULL;
1007 TnyHeader *header = NULL;
1010 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1012 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1014 /* If we already has a model attached then the message shown by
1015 * msg-view is in it, and thus we do not need any actions but
1016 * to check the dimming rules.*/
1017 if(priv->header_model != NULL){
1018 if (priv->row_reference) {
1019 gtk_tree_row_reference_free(priv->next_row_reference);
1020 priv->next_row_reference = gtk_tree_row_reference_copy(
1021 priv->row_reference);
1022 select_next_valid_row (priv->header_model,
1023 &(priv->next_row_reference), FALSE);
1025 modest_ui_actions_check_toolbar_dimming_rules (
1026 MODEST_WINDOW (window));
1030 /* Check if the newly inserted message is the same we are actually
1031 * showing. IF not, we should remain detached from the header model
1032 * and thus prev and next toolbarbuttons should remain dimmed. */
1033 gtk_tree_model_get (new_model, tree_iter,
1034 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1);
1035 uid = modest_tny_folder_get_header_unique_id(header);
1036 if(!g_str_equal(priv->msg_uid, uid)){
1038 g_object_unref(G_OBJECT(header));
1042 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1043 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1044 g_object_unref(G_OBJECT(header));
1048 /* Setup row_reference for the actual msg. */
1049 priv->row_reference = gtk_tree_row_reference_new(
1050 new_model, tree_path);
1051 if(priv->row_reference == NULL){
1052 g_warning("No reference for msg header item.");
1056 /* Attach new_model and connect some callback to it to become able
1057 * to detect changes in header-view. */
1058 priv->header_model = g_object_ref(new_model);
1059 g_signal_connect (new_model, "row-changed",
1060 G_CALLBACK (modest_msg_view_window_on_row_changed),
1062 g_signal_connect (new_model, "row-deleted",
1063 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1065 g_signal_connect (new_model, "rows-reordered",
1066 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1069 /* Now set up next_row_reference. */
1070 priv->next_row_reference = gtk_tree_row_reference_copy(
1071 priv->row_reference);
1072 select_next_valid_row (priv->header_model,
1073 &(priv->next_row_reference), FALSE);
1075 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1078 void modest_msg_view_window_on_row_reordered(
1079 GtkTreeModel *header_model,
1083 ModestMsgViewWindow *window){
1084 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1087 /* The modest_msg_view_window_update_model_replaced implements update
1088 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1089 * actually belongs to the header-view is the same as the TnyFolder of
1090 * the message of msg-view or not. If they are different, there is
1091 * nothing to do. If they are the same, then the model has replaced and
1092 * the reference in msg-view shall be replaced from the old model to
1093 * the new model. In this case the view will be detached from it's
1094 * header folder. From this point the next/prev buttons are dimmed.
1096 void modest_msg_view_window_update_model_replaced(
1097 ModestHeaderViewObserver *observer,
1098 GtkTreeModel *model,
1099 const gchar *tny_folder_id){
1100 ModestMsgViewWindowPrivate *priv = NULL;
1101 ModestMsgViewWindow *window = NULL;
1103 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1104 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1106 window = MODEST_MSG_VIEW_WINDOW(observer);
1107 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1109 /* If there is an other folder in the header-view then we do
1110 * not care about it's model (msg list). Else if the
1111 * header-view shows the folder the msg shown by us is in, we
1112 * shall replace our model reference and make some check. */
1113 if(tny_folder_id == NULL || !g_str_equal(tny_folder_id, priv->header_folder_id))
1116 /* Model is changed(replaced), so we should forget the old
1117 * one. Because there might be other references and there
1118 * might be some change on the model even if we unreferenced
1119 * it, we need to disconnect our signals here. */
1120 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1121 priv->row_changed_handler))
1122 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1123 priv->row_changed_handler);
1124 priv->row_changed_handler = 0;
1125 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1126 priv->row_deleted_handler))
1127 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1128 priv->row_deleted_handler);
1129 priv->row_deleted_handler = 0;
1130 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1131 priv->row_inserted_handler))
1132 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1133 priv->row_inserted_handler);
1134 priv->row_inserted_handler = 0;
1135 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1136 priv->rows_reordered_handler))
1137 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1138 priv->rows_reordered_handler);
1139 priv->rows_reordered_handler = 0;
1140 g_object_unref(priv->header_model);
1141 priv->header_model = NULL;
1142 if (priv->row_reference)
1143 g_object_unref(priv->row_reference);
1144 priv->row_reference = NULL;
1145 if (priv->next_row_reference)
1146 g_object_unref(priv->next_row_reference);
1147 priv->next_row_reference = NULL;
1149 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1151 g_assert(model != NULL);
1153 /* Also we must connect to the new model for row insertions.
1154 * Only for insertions now. We will need other ones only after
1155 * the msg is show by msg-view is added to the new model. */
1156 priv->row_inserted_handler = g_signal_connect (
1157 model, "row-inserted",
1158 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1163 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1165 ModestMsgViewWindowPrivate *priv= NULL;
1167 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1168 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1170 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1174 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1176 ModestMsgViewWindowPrivate *priv= NULL;
1178 TnyHeader *header = NULL;
1179 GtkTreePath *path = NULL;
1182 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1183 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1185 /* If the message was not obtained from a treemodel,
1186 * for instance if it was opened directly by the search UI:
1188 if (priv->header_model == NULL || priv->row_reference == NULL) {
1189 msg = modest_msg_view_window_get_message (self);
1191 header = tny_msg_get_header (msg);
1192 g_object_unref (msg);
1197 /* Get iter of the currently selected message in the header view: */
1198 /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage,
1199 * instead of sometimes retrieving it from the header view?
1200 * Then we wouldn't be dependent on the message actually still being selected
1201 * in the header view. murrayc. */
1202 if (!gtk_tree_row_reference_valid (priv->row_reference))
1204 path = gtk_tree_row_reference_get_path (priv->row_reference);
1205 g_return_val_if_fail (path != NULL, NULL);
1206 gtk_tree_model_get_iter (priv->header_model,
1210 /* Get current message header */
1211 gtk_tree_model_get (priv->header_model, &iter,
1212 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1215 gtk_tree_path_free (path);
1220 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1222 ModestMsgViewWindowPrivate *priv;
1224 g_return_val_if_fail (self, NULL);
1226 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1228 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1232 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1234 ModestMsgViewWindowPrivate *priv;
1236 g_return_val_if_fail (self, NULL);
1238 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1240 return (const gchar*) priv->msg_uid;
1244 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1247 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1248 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1249 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1253 is_active = gtk_toggle_action_get_active (toggle);
1256 gtk_widget_show (priv->find_toolbar);
1257 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1259 gtk_widget_hide (priv->find_toolbar);
1262 /* update the toggle buttons status */
1263 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1264 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1265 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1266 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1271 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1272 ModestMsgViewWindow *obj)
1274 GtkToggleAction *toggle;
1275 ModestWindowPrivate *parent_priv;
1276 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1278 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1279 gtk_toggle_action_set_active (toggle, FALSE);
1283 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1284 ModestMsgViewWindow *obj)
1286 gchar *current_search;
1287 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1289 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1290 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1294 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1296 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1297 g_free (current_search);
1298 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1302 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1304 g_free (priv->last_search);
1305 priv->last_search = g_strdup (current_search);
1306 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1309 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1310 g_free (priv->last_search);
1311 priv->last_search = NULL;
1313 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1316 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1317 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1318 g_free (priv->last_search);
1319 priv->last_search = NULL;
1321 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1325 g_free (current_search);
1330 modest_msg_view_window_set_zoom (ModestWindow *window,
1333 ModestMsgViewWindowPrivate *priv;
1334 ModestWindowPrivate *parent_priv;
1335 GtkAction *action = NULL;
1336 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1338 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1340 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1341 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1342 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1344 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1345 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1347 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1351 modest_msg_view_window_get_zoom (ModestWindow *window)
1353 ModestMsgViewWindowPrivate *priv;
1355 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1357 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1358 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1362 modest_msg_view_window_zoom_plus (ModestWindow *window)
1364 ModestWindowPrivate *parent_priv;
1365 GtkRadioAction *zoom_radio_action;
1366 GSList *group, *node;
1368 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1369 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1370 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1372 group = gtk_radio_action_get_group (zoom_radio_action);
1374 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1375 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1379 for (node = group; node != NULL; node = g_slist_next (node)) {
1380 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1381 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1389 modest_msg_view_window_zoom_minus (ModestWindow *window)
1391 ModestWindowPrivate *parent_priv;
1392 GtkRadioAction *zoom_radio_action;
1393 GSList *group, *node;
1395 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1396 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1397 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1399 group = gtk_radio_action_get_group (zoom_radio_action);
1401 for (node = group; node != NULL; node = g_slist_next (node)) {
1402 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1403 if (node->next != NULL) {
1404 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1407 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1417 modest_msg_view_window_key_event (GtkWidget *window,
1423 focus = gtk_window_get_focus (GTK_WINDOW (window));
1425 /* for the find toolbar case */
1426 if (focus && GTK_IS_ENTRY (focus)) {
1427 if (event->keyval == GDK_BackSpace) {
1429 copy = gdk_event_copy ((GdkEvent *) event);
1430 gtk_widget_event (focus, copy);
1431 gdk_event_free (copy);
1436 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1437 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1438 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1439 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1440 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1441 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1442 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1443 gboolean return_value;
1445 if (event->type == GDK_KEY_RELEASE) {
1446 GtkScrollType scroll_type;
1448 switch (event->keyval) {
1451 scroll_type = GTK_SCROLL_STEP_UP; break;
1454 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1456 case GDK_KP_Page_Up:
1457 scroll_type = GTK_SCROLL_PAGE_UP; break;
1459 case GDK_KP_Page_Down:
1460 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1463 scroll_type = GTK_SCROLL_START; break;
1466 scroll_type = GTK_SCROLL_END; break;
1467 default: scroll_type = GTK_SCROLL_NONE;
1470 g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child",
1471 scroll_type, FALSE, &return_value);
1482 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1485 ModestMsgViewWindowPrivate *priv;
1486 GtkTreeIter tmp_iter;
1487 gboolean is_last_selected;
1489 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1490 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1492 /*if no model (so no rows at all), then virtually we are the last*/
1493 if (!priv->header_model || !priv->row_reference)
1496 path = gtk_tree_row_reference_get_path (priv->row_reference);
1500 is_last_selected = TRUE;
1501 while (is_last_selected) {
1503 gtk_tree_path_next (path);
1504 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1506 gtk_tree_model_get (priv->header_model, &tmp_iter,
1507 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1510 if (msg_is_visible (header, priv->is_outbox))
1511 is_last_selected = FALSE;
1512 g_object_unref(G_OBJECT(header));
1515 gtk_tree_path_free (path);
1516 return is_last_selected;
1520 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1522 ModestMsgViewWindowPrivate *priv;
1524 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1527 return priv->header_model != NULL;
1531 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1533 ModestMsgViewWindowPrivate *priv;
1535 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1536 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1538 return priv->is_search_result;
1542 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1544 return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) &&
1545 ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ;
1550 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1553 ModestMsgViewWindowPrivate *priv;
1554 gboolean is_first_selected;
1555 GtkTreeIter tmp_iter;
1556 /* gchar * path_string;*/
1558 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1559 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1561 /*if no model (so no rows at all), then virtually we are the first*/
1562 if (!priv->header_model || !priv->row_reference)
1565 path = gtk_tree_row_reference_get_path (priv->row_reference);
1569 /* path_string = gtk_tree_path_to_string (path);
1570 is_first_selected = strcmp (path_string, "0");
1572 g_free (path_string);
1573 gtk_tree_path_free (path);
1575 return is_first_selected;*/
1577 is_first_selected = TRUE;
1578 while (is_first_selected) {
1580 if(!gtk_tree_path_prev (path))
1582 /* Here the 'if' is needless for logic, but let make sure
1583 * iter is valid for gtk_tree_model_get. */
1584 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1586 gtk_tree_model_get (priv->header_model, &tmp_iter,
1587 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1590 if (msg_is_visible (header, priv->is_outbox))
1591 is_first_selected = FALSE;
1592 g_object_unref(G_OBJECT(header));
1595 gtk_tree_path_free (path);
1596 return is_first_selected;
1600 * Reads the message whose summary item is @header. It takes care of
1601 * several things, among others:
1603 * If the message was not previously downloaded then ask the user
1604 * before downloading. If there is no connection launch the connection
1605 * dialog. Update toolbar dimming rules.
1607 * Returns: TRUE if the mail operation was started, otherwise if the
1608 * user do not want to download the message, or if the user do not
1609 * want to connect, then the operation is not issued
1612 message_reader (ModestMsgViewWindow *window,
1613 ModestMsgViewWindowPrivate *priv,
1615 GtkTreeRowReference *row_reference)
1617 ModestMailOperation *mail_op = NULL;
1618 gboolean already_showing = FALSE;
1619 ModestWindow *msg_window = NULL;
1620 ModestWindowMgr *mgr;
1622 g_return_val_if_fail (row_reference != NULL, FALSE);
1624 mgr = modest_runtime_get_window_mgr ();
1625 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1626 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1629 gtk_window_present (GTK_WINDOW (msg_window));
1630 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1634 /* Msg download completed */
1635 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1636 /* Ask the user if he wants to download the message if
1638 if (!tny_device_is_online (modest_runtime_get_device())) {
1639 TnyFolder *folder = NULL;
1640 GtkResponseType response;
1642 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1643 _("mcen_nc_get_msg"));
1644 if (response == GTK_RESPONSE_CANCEL)
1647 /* Offer the connection dialog if necessary */
1648 folder = tny_header_get_folder (header);
1650 if (!modest_platform_connect_and_wait_if_network_folderstore (NULL,
1651 TNY_FOLDER_STORE (folder))) {
1652 g_object_unref (folder);
1655 g_object_unref (folder);
1660 /* New mail operation */
1661 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(window),
1662 modest_ui_actions_get_msgs_full_error_handler,
1665 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1666 modest_mail_operation_get_msg (mail_op, header, view_msg_cb, row_reference);
1667 g_object_unref (mail_op);
1669 /* Update dimming rules */
1670 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1671 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1677 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1679 ModestMsgViewWindowPrivate *priv;
1680 GtkTreePath *path= NULL;
1681 GtkTreeIter tmp_iter;
1683 gboolean retval = TRUE;
1684 GtkTreeRowReference *row_reference = NULL;
1686 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1687 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1689 if (!priv->row_reference)
1692 /* Update the next row reference if it's not valid. This could
1693 happen if for example the header which it was pointing to,
1694 was deleted. The best place to do it is in the row-deleted
1695 handler but the tinymail model do not work like the glib
1696 tree models and reports the deletion when the row is still
1698 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1699 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1700 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1701 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE);
1704 if (priv->next_row_reference)
1705 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1709 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1711 gtk_tree_model_get_iter (priv->header_model,
1714 gtk_tree_path_free (path);
1716 gtk_tree_model_get (priv->header_model, &tmp_iter,
1717 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1720 /* Read the message & show it */
1721 if (!message_reader (window, priv, header, row_reference)) {
1723 gtk_tree_row_reference_free (row_reference);
1727 g_object_unref (header);
1733 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1735 ModestMsgViewWindowPrivate *priv = NULL;
1737 gboolean finished = FALSE;
1738 gboolean retval = FALSE;
1740 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1741 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1743 /* Return inmediatly if there is no header model */
1744 if (!priv->header_model || !priv->row_reference)
1747 path = gtk_tree_row_reference_get_path (priv->row_reference);
1748 while (!finished && gtk_tree_path_prev (path)) {
1752 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1753 gtk_tree_model_get (priv->header_model, &iter,
1754 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1758 if (msg_is_visible (header, priv->is_outbox)) {
1759 GtkTreeRowReference *row_reference;
1760 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1761 /* Read the message & show it */
1762 retval = message_reader (window, priv, header, row_reference);
1764 gtk_tree_row_reference_free (row_reference);
1768 g_object_unref (header);
1772 gtk_tree_path_free (path);
1777 view_msg_cb (ModestMailOperation *mail_op,
1784 ModestMsgViewWindow *self = NULL;
1785 ModestMsgViewWindowPrivate *priv = NULL;
1786 GtkTreeRowReference *row_reference = NULL;
1789 g_object_unref (self);
1793 /* If there was any error */
1794 row_reference = (GtkTreeRowReference *) user_data;
1795 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1796 gtk_tree_row_reference_free (row_reference);
1800 /* Get the window */
1801 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1802 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1803 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1805 /* Update the row reference */
1806 if (priv->row_reference != NULL) {
1807 gtk_tree_row_reference_free (priv->row_reference);
1808 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1809 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1810 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE);
1811 gtk_tree_row_reference_free (row_reference);
1814 /* Mark header as read */
1815 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1816 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1818 /* Set new message */
1819 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1820 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1821 modest_msg_view_window_update_priority (self);
1822 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1823 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1826 /* Set the new message uid of the window */
1827 if (priv->msg_uid) {
1828 g_free (priv->msg_uid);
1829 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1832 /* Notify the observers */
1833 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1834 0, priv->header_model, priv->row_reference);
1836 /* Free new references */
1837 g_object_unref (self);
1841 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1843 ModestMsgViewWindowPrivate *priv;
1845 TnyFolderType folder_type;
1847 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1849 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1851 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1855 folder = tny_msg_get_folder (msg);
1857 folder_type = tny_folder_get_folder_type (folder);
1858 g_object_unref (folder);
1860 g_object_unref (msg);
1868 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1870 ModestMsgViewWindowPrivate *priv;
1871 TnyHeader *header = NULL;
1872 TnyHeaderFlags flags = 0;
1874 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1876 if (priv->header_model && priv->row_reference) {
1878 GtkTreePath *path = NULL;
1880 path = gtk_tree_row_reference_get_path (priv->row_reference);
1881 g_return_if_fail (path != NULL);
1882 gtk_tree_model_get_iter (priv->header_model,
1884 gtk_tree_row_reference_get_path (priv->row_reference));
1886 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1888 gtk_tree_path_free (path);
1891 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1893 header = tny_msg_get_header (msg);
1894 g_object_unref (msg);
1899 flags = tny_header_get_flags (header);
1900 g_object_unref(G_OBJECT(header));
1903 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1908 toolbar_resize (ModestMsgViewWindow *self)
1910 ModestMsgViewWindowPrivate *priv = NULL;
1911 ModestWindowPrivate *parent_priv = NULL;
1913 gint static_button_size;
1914 ModestWindowMgr *mgr;
1916 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1917 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1918 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1920 mgr = modest_runtime_get_window_mgr ();
1921 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
1923 if (parent_priv->toolbar) {
1924 /* left size buttons */
1925 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
1926 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1927 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1928 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1929 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
1930 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1931 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1932 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1933 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
1934 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1935 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1936 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1937 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1938 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
1939 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
1940 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
1942 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
1943 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
1944 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1945 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
1946 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1947 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
1948 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1949 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
1955 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
1957 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
1958 ModestWindowPrivate *parent_priv;
1959 ModestWindowMgr *mgr;
1960 gboolean is_fullscreen;
1961 GtkAction *fs_toggle_action;
1964 mgr = modest_runtime_get_window_mgr ();
1965 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
1967 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
1969 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
1970 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
1971 if (is_fullscreen != active) {
1972 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
1974 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
1982 modest_msg_view_window_show_toolbar (ModestWindow *self,
1983 gboolean show_toolbar)
1985 ModestMsgViewWindowPrivate *priv = NULL;
1986 ModestWindowPrivate *parent_priv;
1987 GtkWidget *reply_button = NULL, *menu = NULL;
1988 GtkWidget *placeholder = NULL;
1990 const gchar *action_name;
1993 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
1994 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1996 /* Set optimized view status */
1997 priv->optimized_view = !show_toolbar;
1999 if (!parent_priv->toolbar) {
2000 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2002 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2004 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2005 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2006 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2007 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2008 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2010 /* Add ProgressBar (Transfer toolbar) */
2011 priv->progress_bar = modest_progress_bar_widget_new ();
2012 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2013 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2014 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2015 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2016 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2018 /* Connect cancel 'clicked' signal to abort progress mode */
2019 g_signal_connect(priv->cancel_toolitem, "clicked",
2020 G_CALLBACK(cancel_progressbar),
2023 /* Add it to the observers list */
2024 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2027 hildon_window_add_toolbar (HILDON_WINDOW (self),
2028 GTK_TOOLBAR (parent_priv->toolbar));
2030 /* Set reply button tap and hold menu */
2031 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2032 "/ToolBar/ToolbarMessageReply");
2033 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2034 "/ToolbarReplyCSM");
2035 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2039 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2040 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2041 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2043 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2044 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2045 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2047 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2050 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2051 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2054 /* Update also the actions (to update the toggles in the
2055 menus), we have to do it manually because some other window
2056 of the same time could have changed it (remember that the
2057 toolbar fullscreen mode is shared by all the windows of the
2059 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2060 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2062 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2064 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2065 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2070 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2072 ModestMsgViewWindow *window)
2074 if (!GTK_WIDGET_VISIBLE (window))
2077 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2081 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2083 ModestMsgViewWindowPrivate *priv;
2085 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2086 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2088 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2092 cancel_progressbar (GtkToolButton *toolbutton,
2093 ModestMsgViewWindow *self)
2096 ModestMsgViewWindowPrivate *priv;
2098 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2100 /* Get operation observers and cancel its current operation */
2101 tmp = priv->progress_widgets;
2103 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2104 tmp=g_slist_next(tmp);
2108 observers_empty (ModestMsgViewWindow *self)
2111 ModestMsgViewWindowPrivate *priv;
2112 gboolean is_empty = TRUE;
2113 guint pending_ops = 0;
2115 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2116 tmp = priv->progress_widgets;
2118 /* Check all observers */
2119 while (tmp && is_empty) {
2120 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2121 is_empty = pending_ops == 0;
2123 tmp = g_slist_next(tmp);
2130 on_account_removed (TnyAccountStore *account_store,
2131 TnyAccount *account,
2134 /* Do nothing if it's a transport account, because we only
2135 show the messages of a store account */
2136 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2137 const gchar *parent_acc = NULL;
2138 const gchar *our_acc = NULL;
2140 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2141 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2143 /* Close this window if I'm showing a message of the removed account */
2144 if (strcmp (parent_acc, our_acc) == 0)
2145 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2150 on_mail_operation_started (ModestMailOperation *mail_op,
2153 ModestMsgViewWindow *self;
2154 ModestMailOperationTypeOperation op_type;
2156 ModestMsgViewWindowPrivate *priv;
2157 GObject *source = NULL;
2159 self = MODEST_MSG_VIEW_WINDOW (user_data);
2160 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2161 op_type = modest_mail_operation_get_type_operation (mail_op);
2162 tmp = priv->progress_widgets;
2163 source = modest_mail_operation_get_source(mail_op);
2164 if (G_OBJECT (self) == source) {
2165 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2166 set_toolbar_transfer_mode(self);
2168 modest_progress_object_add_operation (
2169 MODEST_PROGRESS_OBJECT (tmp->data),
2171 tmp = g_slist_next (tmp);
2175 g_object_unref (source);
2179 on_mail_operation_finished (ModestMailOperation *mail_op,
2182 ModestMsgViewWindow *self;
2183 ModestMailOperationTypeOperation op_type;
2185 ModestMsgViewWindowPrivate *priv;
2187 self = MODEST_MSG_VIEW_WINDOW (user_data);
2188 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2189 op_type = modest_mail_operation_get_type_operation (mail_op);
2190 tmp = priv->progress_widgets;
2192 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2194 modest_progress_object_remove_operation (
2195 MODEST_PROGRESS_OBJECT (tmp->data),
2197 tmp = g_slist_next (tmp);
2200 /* If no more operations are being observed, NORMAL mode is enabled again */
2201 if (observers_empty (self)) {
2202 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2208 on_queue_changed (ModestMailOperationQueue *queue,
2209 ModestMailOperation *mail_op,
2210 ModestMailOperationQueueNotification type,
2211 ModestMsgViewWindow *self)
2213 ModestMsgViewWindowPrivate *priv;
2215 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2217 /* If this operations was created by another window, do nothing */
2218 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2221 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2222 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2224 "operation-started",
2225 G_CALLBACK (on_mail_operation_started),
2227 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2229 "operation-finished",
2230 G_CALLBACK (on_mail_operation_finished),
2232 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2233 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2235 "operation-started");
2236 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2238 "operation-finished");
2243 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2245 ModestMsgViewWindowPrivate *priv;
2246 TnyList *selected_attachments = NULL;
2248 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2249 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2251 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2253 return selected_attachments;
2257 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2259 ModestMsgViewWindowPrivate *priv;
2260 const gchar *msg_uid;
2261 gchar *attachment_uid = NULL;
2262 gint attachment_index = 0;
2263 TnyList *attachments;
2265 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2266 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2267 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2269 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2270 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2271 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2272 g_object_unref (attachments);
2274 if (msg_uid && attachment_index >= 0) {
2275 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2278 if (mime_part == NULL) {
2279 gboolean error = FALSE;
2280 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2281 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2283 } else if (tny_list_get_length (selected_attachments) > 1) {
2284 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2288 iter = tny_list_create_iterator (selected_attachments);
2289 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2290 g_object_unref (iter);
2292 g_object_unref (selected_attachments);
2297 g_object_ref (mime_part);
2300 if (tny_mime_part_is_purged (mime_part)) {
2301 g_object_unref (mime_part);
2305 if (!TNY_IS_MSG (mime_part)) {
2306 gchar *filepath = NULL;
2307 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2308 const gchar *content_type;
2309 gboolean show_error_banner = FALSE;
2311 TnyFsStream *temp_stream = NULL;
2312 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2315 if (temp_stream != NULL) {
2316 content_type = tny_mime_part_get_content_type (mime_part);
2317 if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2318 /* make the file read-only */
2319 if (g_chmod(filepath, 0444) != 0)
2320 g_warning ("%s: failed to set file '%s' to read-only: %s",
2321 __FUNCTION__, filepath, strerror(errno));
2323 modest_platform_activate_file (filepath, content_type);
2325 /* error while saving attachment, maybe cerm_device_memory_full */
2326 show_error_banner = TRUE;
2328 g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2332 g_object_unref (temp_stream);
2334 /* NOTE: files in the temporary area will be automatically
2335 * cleaned after some time if they are no longer in use */
2337 if (filepath != NULL) {
2338 /* the file may already exist but it isn't writable,
2339 * let's try to open it anyway */
2340 content_type = tny_mime_part_get_content_type (mime_part);
2341 modest_platform_activate_file (filepath, content_type);
2344 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2345 show_error_banner = TRUE;
2348 if (show_error_banner)
2349 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2351 /* message attachment */
2352 TnyHeader *header = NULL;
2353 ModestWindowMgr *mgr;
2354 ModestWindow *msg_win = NULL;
2357 header = tny_msg_get_header (TNY_MSG (mime_part));
2358 mgr = modest_runtime_get_window_mgr ();
2359 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2362 if (msg_win) /* there is already a window for this uid; top it */
2363 gtk_window_present (GTK_WINDOW(msg_win));
2365 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2366 * thus, we don't do anything */
2367 g_warning ("window for is already being created");
2369 /* it's not found, so create a new window for it */
2370 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2371 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2373 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2374 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2375 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2376 modest_window_get_zoom (MODEST_WINDOW (window)));
2377 modest_window_mgr_register_window (mgr, msg_win);
2378 gtk_widget_show_all (GTK_WIDGET (msg_win));
2381 g_object_unref (mime_part);
2397 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2398 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2399 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2400 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2403 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2407 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2408 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2409 g_free (pair->filename);
2410 g_object_unref (pair->part);
2411 g_slice_free (SaveMimePartPair, pair);
2413 g_list_free (info->pairs);
2416 gtk_widget_destroy (info->banner);
2417 g_object_unref (info->banner);
2418 g_slice_free (SaveMimePartInfo, info);
2423 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2425 if (info->pairs != NULL) {
2426 save_mime_part_to_file (info);
2429 result = info->result;
2431 /* This is a GDK lock because we are an idle callback and
2432 * hildon_banner_show_information is or does Gtk+ code */
2434 gdk_threads_enter (); /* CHECKED */
2435 save_mime_part_info_free (info, TRUE);
2437 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2439 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2441 gdk_threads_leave (); /* CHECKED */
2448 save_mime_part_to_file (SaveMimePartInfo *info)
2450 GnomeVFSResult result;
2451 GnomeVFSHandle *handle;
2453 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2454 gboolean decode_result = TRUE;
2456 result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2457 if (result == GNOME_VFS_OK) {
2458 stream = tny_vfs_stream_new (handle);
2459 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2460 decode_result = FALSE;
2462 g_object_unref (G_OBJECT (stream));
2463 g_object_unref (pair->part);
2464 g_slice_free (SaveMimePartPair, pair);
2465 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2466 info->result = decode_result;
2468 save_mime_part_info_free (info, FALSE);
2469 info->result = FALSE;
2472 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2477 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2479 gboolean is_ok = TRUE;
2480 gint replaced_files = 0;
2481 const GList *files = info->pairs;
2484 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2485 SaveMimePartPair *pair = iter->data;
2486 if (modest_utils_file_exists (pair->filename)) {
2490 if (replaced_files) {
2491 GtkWidget *confirm_overwrite_dialog;
2492 const gchar *message = (replaced_files == 1) ?
2493 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2494 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2495 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2498 gtk_widget_destroy (confirm_overwrite_dialog);
2502 save_mime_part_info_free (info, TRUE);
2504 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2505 _CS("sfil_ib_saving"));
2506 info->banner = g_object_ref (banner);
2507 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2508 g_object_unref (banner);
2515 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2517 ModestMsgViewWindowPrivate *priv;
2518 GList *files_to_save = NULL;
2519 GtkWidget *save_dialog = NULL;
2520 gchar *folder = NULL;
2521 gboolean canceled = FALSE;
2522 const gchar *filename = NULL;
2523 gchar *save_multiple_str = NULL;
2525 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2526 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2528 if (mime_parts == NULL) {
2529 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2530 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2533 g_object_ref (mime_parts);
2536 /* prepare dialog */
2537 if (tny_list_get_length (mime_parts) == 1) {
2539 /* only one attachment selected */
2540 iter = tny_list_create_iterator (mime_parts);
2541 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2542 g_object_unref (iter);
2543 if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) {
2544 filename = tny_mime_part_get_filename (mime_part);
2546 g_warning ("Tried to save a non-file attachment");
2549 g_object_unref (mime_part);
2551 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2552 tny_list_get_length (mime_parts));
2555 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2556 GTK_FILE_CHOOSER_ACTION_SAVE);
2559 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2560 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2564 if (filename != NULL)
2565 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2568 /* if multiple, set multiple string */
2569 if (save_multiple_str) {
2570 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2574 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2575 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2577 if (!modest_utils_folder_writable (chooser_uri)) {
2578 hildon_banner_show_information
2579 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2583 iter = tny_list_create_iterator (mime_parts);
2584 while (!tny_iterator_is_done (iter)) {
2585 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2587 tny_iterator_next (iter);
2588 if (tny_mime_part_is_attachment (mime_part)) {
2589 SaveMimePartPair *pair;
2591 if ((!tny_iterator_is_done (iter)) &&
2592 (tny_mime_part_get_filename (mime_part) == NULL)) {
2593 g_object_unref (mime_part);
2597 pair = g_slice_new0 (SaveMimePartPair);
2598 if (tny_iterator_is_done (iter)) {
2599 pair->filename = g_strdup (chooser_uri);
2602 g_build_filename (chooser_uri,
2603 tny_mime_part_get_filename (mime_part), NULL);
2605 pair->part = mime_part;
2606 files_to_save = g_list_prepend (files_to_save, pair);
2609 g_object_unref (iter);
2611 g_free (chooser_uri);
2614 gtk_widget_destroy (save_dialog);
2616 g_object_unref (mime_parts);
2618 if (files_to_save != NULL) {
2619 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2620 info->pairs = files_to_save;
2621 info->result = TRUE;
2622 save_mime_parts_to_file_with_checks (info);
2627 show_remove_attachment_information (gpointer userdata)
2629 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2630 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2632 /* We're outside the main lock */
2633 gdk_threads_enter ();
2635 if (priv->remove_attachment_banner != NULL) {
2636 gtk_widget_destroy (priv->remove_attachment_banner);
2637 g_object_unref (priv->remove_attachment_banner);
2640 priv->remove_attachment_banner = g_object_ref (
2641 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2643 gdk_threads_leave ();
2649 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2651 ModestMsgViewWindowPrivate *priv;
2652 TnyList *mime_parts = NULL;
2653 gchar *confirmation_message;
2658 /* TnyFolder *folder; */
2660 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2661 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2664 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2666 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2668 /* Remove already purged messages from mime parts list */
2669 iter = tny_list_create_iterator (mime_parts);
2670 while (!tny_iterator_is_done (iter)) {
2671 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2672 tny_iterator_next (iter);
2673 if (tny_mime_part_is_purged (part)) {
2674 tny_list_remove (mime_parts, (GObject *) part);
2676 g_object_unref (part);
2678 g_object_unref (iter);
2680 if (tny_list_get_length (mime_parts) == 0) {
2681 g_object_unref (mime_parts);
2685 n_attachments = tny_list_get_length (mime_parts);
2686 if (n_attachments == 1) {
2687 const gchar *filename;
2690 iter = tny_list_create_iterator (mime_parts);
2691 part = (TnyMimePart *) tny_iterator_get_current (iter);
2692 g_object_unref (iter);
2693 if (TNY_IS_MSG (part)) {
2695 header = tny_msg_get_header (TNY_MSG (part));
2696 filename = tny_header_get_subject (header);
2697 g_object_unref (header);
2698 if (filename == NULL)
2699 filename = _("mail_va_no_subject");
2701 filename = tny_mime_part_get_filename (TNY_MIME_PART (part));
2703 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2704 g_object_unref (part);
2706 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2707 "mcen_nc_purge_files_text",
2708 n_attachments), n_attachments);
2710 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2711 confirmation_message);
2712 g_free (confirmation_message);
2714 if (response != GTK_RESPONSE_OK) {
2715 g_object_unref (mime_parts);
2719 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2720 /* folder = tny_msg_get_folder (msg); */
2721 /* tny_msg_uncache_attachments (msg); */
2722 /* tny_folder_refresh (folder, NULL); */
2723 /* g_object_unref (folder); */
2725 iter = tny_list_create_iterator (mime_parts);
2726 while (!tny_iterator_is_done (iter)) {
2729 part = (TnyMimePart *) tny_iterator_get_current (iter);
2730 tny_mime_part_set_purged (TNY_MIME_PART (part));
2731 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2732 g_object_unref (part);
2733 tny_iterator_next (iter);
2735 g_object_unref (iter);
2737 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2738 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2739 tny_msg_rewrite_cache (msg);
2740 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2741 g_object_unref (msg);
2743 g_object_unref (mime_parts);
2745 if (priv->purge_timeout > 0) {
2746 g_source_remove (priv->purge_timeout);
2747 priv->purge_timeout = 0;
2750 if (priv->remove_attachment_banner) {
2751 gtk_widget_destroy (priv->remove_attachment_banner);
2752 g_object_unref (priv->remove_attachment_banner);
2753 priv->remove_attachment_banner = NULL;
2761 update_window_title (ModestMsgViewWindow *window)
2763 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2765 TnyHeader *header = NULL;
2766 const gchar *subject = NULL;
2768 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2771 header = tny_msg_get_header (msg);
2772 subject = tny_header_get_subject (header);
2773 g_object_unref (msg);
2776 if ((subject == NULL)||(subject[0] == '\0'))
2777 subject = _("mail_va_no_subject");
2779 gtk_window_set_title (GTK_WINDOW (window), subject);
2783 static void on_move_focus (GtkWidget *widget,
2784 GtkDirectionType direction,
2787 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");