1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar.h"
51 #include "hildon/hildon-pannable-area.h"
52 #include "modest-defs.h"
53 #include "modest-hildon-includes.h"
54 #include "modest-ui-dimming-manager.h"
55 #include <gdk/gdkkeysyms.h>
56 #include <modest-tny-account.h>
57 #include <modest-mime-part-view.h>
58 #include <modest-isearch-view.h>
59 #include <modest-tny-mime-part.h>
62 #include <glib/gstdio.h>
63 #include <modest-debug.h>
65 #define DEFAULT_FOLDER "MyDocs/.documents"
67 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
68 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
69 static void modest_header_view_observer_init(
70 ModestHeaderViewObserverIface *iface_class);
71 static void modest_msg_view_window_finalize (GObject *obj);
72 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
74 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
75 ModestMsgViewWindow *obj);
76 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
77 ModestMsgViewWindow *obj);
79 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
81 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
82 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
86 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
88 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
89 gboolean show_toolbar);
91 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
93 ModestMsgViewWindow *window);
95 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
98 ModestMsgViewWindow *window);
100 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
102 ModestMsgViewWindow *window);
104 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
105 GtkTreePath *tree_path,
106 GtkTreeIter *tree_iter,
107 ModestMsgViewWindow *window);
109 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
113 ModestMsgViewWindow *window);
115 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
117 const gchar *tny_folder_id);
119 static void cancel_progressbar (GtkToolButton *toolbutton,
120 ModestMsgViewWindow *self);
122 static void on_queue_changed (ModestMailOperationQueue *queue,
123 ModestMailOperation *mail_op,
124 ModestMailOperationQueueNotification type,
125 ModestMsgViewWindow *self);
127 static void on_account_removed (TnyAccountStore *account_store,
131 static void on_move_focus (GtkWidget *widget,
132 GtkDirectionType direction,
135 static void view_msg_cb (ModestMailOperation *mail_op,
142 static void set_toolbar_mode (ModestMsgViewWindow *self,
143 ModestToolBarModes mode);
145 static void update_window_title (ModestMsgViewWindow *window);
147 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
148 static void init_window (ModestMsgViewWindow *obj);
150 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
152 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
154 static gboolean on_fetch_image (ModestMsgView *msgview,
157 ModestMsgViewWindow *window);
159 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
160 GtkScrollType scroll_type,
164 /* list my signals */
171 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
172 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
175 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
176 struct _ModestMsgViewWindowPrivate {
179 GtkWidget *main_scroll;
180 GtkWidget *find_toolbar;
183 /* Progress observers */
184 GtkWidget *progress_bar;
185 GSList *progress_widgets;
188 GtkWidget *progress_toolitem;
189 GtkWidget *cancel_toolitem;
190 GtkWidget *prev_toolitem;
191 GtkWidget *next_toolitem;
192 ModestToolBarModes current_toolbar_mode;
194 /* Optimized view enabled */
195 gboolean optimized_view;
197 /* Whether this was created via the *_new_for_search_result() function. */
198 gboolean is_search_result;
200 /* Whether the message is in outbox */
203 /* A reference to the @model of the header view
204 * to allow selecting previous/next messages,
205 * if the message is currently selected in the header view.
207 const gchar *header_folder_id;
208 GtkTreeModel *header_model;
209 GtkTreeRowReference *row_reference;
210 GtkTreeRowReference *next_row_reference;
212 gulong clipboard_change_handler;
213 gulong queue_change_handler;
214 gulong account_removed_handler;
215 gulong row_changed_handler;
216 gulong row_deleted_handler;
217 gulong row_inserted_handler;
218 gulong rows_reordered_handler;
221 GtkWidget *remove_attachment_banner;
223 guint progress_bar_timeout;
230 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
231 MODEST_TYPE_MSG_VIEW_WINDOW, \
232 ModestMsgViewWindowPrivate))
234 static GtkWindowClass *parent_class = NULL;
236 /* uncomment the following if you have defined any signals */
237 static guint signals[LAST_SIGNAL] = {0};
240 modest_msg_view_window_get_type (void)
242 static GType my_type = 0;
244 static const GTypeInfo my_info = {
245 sizeof(ModestMsgViewWindowClass),
246 NULL, /* base init */
247 NULL, /* base finalize */
248 (GClassInitFunc) modest_msg_view_window_class_init,
249 NULL, /* class finalize */
250 NULL, /* class data */
251 sizeof(ModestMsgViewWindow),
253 (GInstanceInitFunc) modest_msg_view_window_init,
256 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
257 "ModestMsgViewWindow",
260 static const GInterfaceInfo modest_header_view_observer_info =
262 (GInterfaceInitFunc) modest_header_view_observer_init,
263 NULL, /* interface_finalize */
264 NULL /* interface_data */
267 g_type_add_interface_static (my_type,
268 MODEST_TYPE_HEADER_VIEW_OBSERVER,
269 &modest_header_view_observer_info);
275 save_state (ModestWindow *self)
277 modest_widget_memory_save (modest_runtime_get_conf (),
279 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
283 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
284 GtkScrollType scroll_type,
288 ModestMsgViewWindowPrivate *priv;
289 gboolean return_value;
291 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
292 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
297 add_scroll_binding (GtkBindingSet *binding_set,
299 GtkScrollType scroll)
301 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
303 gtk_binding_entry_add_signal (binding_set, keyval, 0,
305 GTK_TYPE_SCROLL_TYPE, scroll,
306 G_TYPE_BOOLEAN, FALSE);
307 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
309 GTK_TYPE_SCROLL_TYPE, scroll,
310 G_TYPE_BOOLEAN, FALSE);
314 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
316 GObjectClass *gobject_class;
317 ModestWindowClass *modest_window_class;
318 GtkBindingSet *binding_set;
320 gobject_class = (GObjectClass*) klass;
321 modest_window_class = (ModestWindowClass *) klass;
323 parent_class = g_type_class_peek_parent (klass);
324 gobject_class->finalize = modest_msg_view_window_finalize;
326 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
327 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
328 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
330 modest_window_class->save_state_func = save_state;
332 klass->scroll_child = modest_msg_view_window_scroll_child;
334 signals[MSG_CHANGED_SIGNAL] =
335 g_signal_new ("msg-changed",
336 G_TYPE_FROM_CLASS (gobject_class),
338 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
340 modest_marshal_VOID__POINTER_POINTER,
341 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
343 signals[SCROLL_CHILD_SIGNAL] =
344 g_signal_new ("scroll-child",
345 G_TYPE_FROM_CLASS (gobject_class),
346 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
347 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
349 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
350 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
352 binding_set = gtk_binding_set_by_class (klass);
353 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
354 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
355 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
356 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
357 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
358 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
360 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
364 static void modest_header_view_observer_init(
365 ModestHeaderViewObserverIface *iface_class)
367 iface_class->update_func = modest_msg_view_window_update_model_replaced;
371 modest_msg_view_window_init (ModestMsgViewWindow *obj)
373 ModestMsgViewWindowPrivate *priv;
374 ModestWindowPrivate *parent_priv = NULL;
375 GtkActionGroup *action_group = NULL;
376 GError *error = NULL;
377 GdkPixbuf *window_icon;
379 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
380 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
381 parent_priv->ui_manager = gtk_ui_manager_new();
383 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
384 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
386 /* Add common actions */
387 gtk_action_group_add_actions (action_group,
388 modest_action_entries,
389 G_N_ELEMENTS (modest_action_entries),
391 gtk_action_group_add_toggle_actions (action_group,
392 msg_view_toggle_action_entries,
393 G_N_ELEMENTS (msg_view_toggle_action_entries),
396 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
397 g_object_unref (action_group);
399 /* Load the UI definition */
400 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
403 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
404 g_error_free (error);
409 /* Add accelerators */
410 gtk_window_add_accel_group (GTK_WINDOW (obj),
411 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
413 priv->is_search_result = FALSE;
414 priv->is_outbox = FALSE;
416 priv->msg_view = NULL;
417 priv->header_model = NULL;
418 priv->header_folder_id = NULL;
419 priv->clipboard_change_handler = 0;
420 priv->queue_change_handler = 0;
421 priv->account_removed_handler = 0;
422 priv->row_changed_handler = 0;
423 priv->row_deleted_handler = 0;
424 priv->row_inserted_handler = 0;
425 priv->rows_reordered_handler = 0;
426 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
428 priv->optimized_view = FALSE;
429 priv->progress_bar_timeout = 0;
430 priv->purge_timeout = 0;
431 priv->remove_attachment_banner = NULL;
432 priv->msg_uid = NULL;
434 priv->sighandlers = NULL;
437 init_window (MODEST_MSG_VIEW_WINDOW(obj));
439 /* Set window icon */
440 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
442 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
443 g_object_unref (window_icon);
446 hildon_program_add_window (hildon_program_get_instance(),
449 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
450 GTK_WINDOW(obj),"applications_email_viewer");
455 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
457 ModestMsgViewWindowPrivate *priv = NULL;
459 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
461 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
463 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
465 if (priv->progress_bar_timeout > 0) {
466 g_source_remove (priv->progress_bar_timeout);
467 priv->progress_bar_timeout = 0;
474 set_toolbar_mode (ModestMsgViewWindow *self,
475 ModestToolBarModes mode)
477 ModestWindowPrivate *parent_priv;
478 ModestMsgViewWindowPrivate *priv;
479 /* GtkWidget *widget = NULL; */
481 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
483 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
484 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
486 /* Sets current toolbar mode */
487 priv->current_toolbar_mode = mode;
489 /* Update toolbar dimming state */
490 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
493 case TOOLBAR_MODE_NORMAL:
494 if (priv->progress_toolitem) {
495 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
496 gtk_widget_hide (priv->progress_toolitem);
499 if (priv->progress_bar)
500 gtk_widget_hide (priv->progress_bar);
502 if (priv->cancel_toolitem)
503 gtk_widget_hide (priv->cancel_toolitem);
505 if (priv->prev_toolitem)
506 gtk_widget_show (priv->prev_toolitem);
508 if (priv->next_toolitem)
509 gtk_widget_show (priv->next_toolitem);
511 /* Hide toolbar if optimized view is enabled */
512 if (priv->optimized_view) {
513 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
514 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
518 case TOOLBAR_MODE_TRANSFER:
519 if (priv->prev_toolitem)
520 gtk_widget_hide (priv->prev_toolitem);
522 if (priv->next_toolitem)
523 gtk_widget_hide (priv->next_toolitem);
525 if (priv->progress_bar)
526 gtk_widget_show (priv->progress_bar);
528 if (priv->progress_toolitem) {
529 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
530 gtk_widget_show (priv->progress_toolitem);
533 if (priv->cancel_toolitem)
534 gtk_widget_show (priv->cancel_toolitem);
536 /* Show toolbar if it's hiden (optimized view ) */
537 if (priv->optimized_view) {
538 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
539 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
544 g_return_if_reached ();
551 init_window (ModestMsgViewWindow *obj)
553 GtkWidget *main_vbox;
554 ModestMsgViewWindowPrivate *priv;
556 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
558 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
559 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
560 main_vbox = gtk_vbox_new (FALSE, 6);
561 #ifdef MODEST_TOOLKIT_HILDON2
562 priv->main_scroll = hildon_pannable_area_new ();
563 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
565 #ifdef MODEST_USE_MOZEMBED
566 priv->main_scroll = priv->msg_view;
567 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
569 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
570 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
572 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
573 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
574 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
577 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
578 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
580 priv->find_toolbar = hildon_find_toolbar_new (NULL);
581 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
582 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
584 gtk_widget_show_all (GTK_WIDGET(main_vbox));
588 modest_msg_view_window_disconnect_signals (ModestWindow *self)
590 ModestMsgViewWindowPrivate *priv;
591 ModestHeaderView *header_view = NULL;
592 ModestWindow *main_window = NULL;
594 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
596 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
597 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
598 priv->clipboard_change_handler))
599 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
600 priv->clipboard_change_handler);
602 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
603 priv->queue_change_handler))
604 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
605 priv->queue_change_handler);
607 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
608 priv->account_removed_handler))
609 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
610 priv->account_removed_handler);
612 if (priv->header_model) {
613 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
614 priv->row_changed_handler))
615 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
616 priv->row_changed_handler);
618 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
619 priv->row_deleted_handler))
620 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
621 priv->row_deleted_handler);
623 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
624 priv->row_inserted_handler))
625 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
626 priv->row_inserted_handler);
628 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
629 priv->rows_reordered_handler))
630 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
631 priv->rows_reordered_handler);
634 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
635 priv->sighandlers = NULL;
637 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
638 FALSE); /* don't create */
642 header_view = MODEST_HEADER_VIEW(
643 modest_main_window_get_child_widget(
644 MODEST_MAIN_WINDOW(main_window),
645 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
646 if (header_view == NULL)
649 modest_header_view_remove_observer(header_view,
650 MODEST_HEADER_VIEW_OBSERVER(self));
654 modest_msg_view_window_finalize (GObject *obj)
656 ModestMsgViewWindowPrivate *priv;
658 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
660 /* Sanity check: shouldn't be needed, the window mgr should
661 call this function before */
662 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
664 if (priv->header_model != NULL) {
665 g_object_unref (priv->header_model);
666 priv->header_model = NULL;
669 if (priv->progress_bar_timeout > 0) {
670 g_source_remove (priv->progress_bar_timeout);
671 priv->progress_bar_timeout = 0;
674 if (priv->remove_attachment_banner) {
675 gtk_widget_destroy (priv->remove_attachment_banner);
676 g_object_unref (priv->remove_attachment_banner);
677 priv->remove_attachment_banner = NULL;
680 if (priv->purge_timeout > 0) {
681 g_source_remove (priv->purge_timeout);
682 priv->purge_timeout = 0;
685 if (priv->row_reference) {
686 gtk_tree_row_reference_free (priv->row_reference);
687 priv->row_reference = NULL;
690 if (priv->next_row_reference) {
691 gtk_tree_row_reference_free (priv->next_row_reference);
692 priv->next_row_reference = NULL;
696 g_free (priv->msg_uid);
697 priv->msg_uid = NULL;
700 G_OBJECT_CLASS(parent_class)->finalize (obj);
704 select_next_valid_row (GtkTreeModel *model,
705 GtkTreeRowReference **row_reference,
709 GtkTreeIter tmp_iter;
711 GtkTreePath *next = NULL;
712 gboolean retval = FALSE, finished;
714 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
716 path = gtk_tree_row_reference_get_path (*row_reference);
717 gtk_tree_model_get_iter (model, &tmp_iter, path);
718 gtk_tree_row_reference_free (*row_reference);
719 *row_reference = NULL;
723 TnyHeader *header = NULL;
725 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
726 gtk_tree_model_get (model, &tmp_iter,
727 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
731 if (msg_is_visible (header, is_outbox)) {
732 next = gtk_tree_model_get_path (model, &tmp_iter);
733 *row_reference = gtk_tree_row_reference_new (model, next);
734 gtk_tree_path_free (next);
738 g_object_unref (header);
741 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
742 next = gtk_tree_model_get_path (model, &tmp_iter);
744 /* Ensure that we are not selecting the same */
745 if (gtk_tree_path_compare (path, next) != 0) {
746 gtk_tree_model_get (model, &tmp_iter,
747 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
750 if (msg_is_visible (header, is_outbox)) {
751 *row_reference = gtk_tree_row_reference_new (model, next);
755 g_object_unref (header);
759 /* If we ended up in the same message
760 then there is no valid next
764 gtk_tree_path_free (next);
766 /* If there are no more messages and we don't
767 want to start again in the first one then
768 there is no valid next message */
774 gtk_tree_path_free (path);
779 /* TODO: This should be in _init(), with the parameters as properties. */
781 modest_msg_view_window_construct (ModestMsgViewWindow *self,
782 const gchar *modest_account_name,
783 const gchar *msg_uid)
786 ModestMsgViewWindowPrivate *priv = NULL;
787 ModestWindowPrivate *parent_priv = NULL;
788 ModestDimmingRulesGroup *menu_rules_group = NULL;
789 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
790 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
792 obj = G_OBJECT (self);
793 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
794 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
796 priv->msg_uid = g_strdup (msg_uid);
799 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
800 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
801 gtk_widget_show (parent_priv->menubar);
802 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
804 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
805 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
806 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
808 /* Add common dimming rules */
809 modest_dimming_rules_group_add_rules (menu_rules_group,
810 modest_msg_view_menu_dimming_entries,
811 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
812 MODEST_WINDOW (self));
813 modest_dimming_rules_group_add_rules (toolbar_rules_group,
814 modest_msg_view_toolbar_dimming_entries,
815 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
816 MODEST_WINDOW (self));
817 modest_dimming_rules_group_add_rules (clipboard_rules_group,
818 modest_msg_view_clipboard_dimming_entries,
819 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
820 MODEST_WINDOW (self));
822 /* Insert dimming rules group for this window */
823 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
824 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
825 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
826 g_object_unref (menu_rules_group);
827 g_object_unref (toolbar_rules_group);
828 g_object_unref (clipboard_rules_group);
830 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
832 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);
833 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
834 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
835 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
836 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
837 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
838 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
839 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
840 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
841 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
842 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
843 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
844 G_CALLBACK (on_fetch_image), obj);
846 g_signal_connect (G_OBJECT (obj), "key-release-event",
847 G_CALLBACK (modest_msg_view_window_key_event),
850 g_signal_connect (G_OBJECT (obj), "key-press-event",
851 G_CALLBACK (modest_msg_view_window_key_event),
854 g_signal_connect (G_OBJECT (obj), "move-focus",
855 G_CALLBACK (on_move_focus), obj);
857 /* Mail Operation Queue */
858 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
860 G_CALLBACK (on_queue_changed),
863 /* Account manager */
864 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
866 G_CALLBACK(on_account_removed),
869 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
871 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
872 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
873 priv->last_search = NULL;
875 /* Init the clipboard actions dim status */
876 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
878 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
883 /* FIXME: parameter checks */
885 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
886 const gchar *modest_account_name,
887 const gchar *msg_uid,
889 GtkTreeRowReference *row_reference)
891 ModestMsgViewWindow *window = NULL;
892 ModestMsgViewWindowPrivate *priv = NULL;
893 TnyFolder *header_folder = NULL;
894 ModestHeaderView *header_view = NULL;
895 ModestWindow *main_window = NULL;
896 ModestWindowMgr *mgr = NULL;
899 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
902 mgr = modest_runtime_get_window_mgr ();
903 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
904 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
906 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
908 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
910 /* Remember the message list's TreeModel so we can detect changes
911 * and change the list selection when necessary: */
913 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
915 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
916 MODEST_MAIN_WINDOW(main_window),
917 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
920 if (header_view != NULL){
921 header_folder = modest_header_view_get_folder(header_view);
922 /* This could happen if the header folder was
923 unseleted before opening this msg window (for
924 example if the user selects an account in the
925 folder view of the main window */
927 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
928 priv->header_folder_id = tny_folder_get_id(header_folder);
929 g_assert(priv->header_folder_id != NULL);
930 g_object_unref(header_folder);
934 /* Setup row references and connect signals */
935 priv->header_model = g_object_ref (model);
938 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
939 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
940 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
942 priv->row_reference = NULL;
943 priv->next_row_reference = NULL;
946 /* Connect signals */
947 priv->row_changed_handler =
948 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
949 G_CALLBACK(modest_msg_view_window_on_row_changed),
951 priv->row_deleted_handler =
952 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
953 G_CALLBACK(modest_msg_view_window_on_row_deleted),
955 priv->row_inserted_handler =
956 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
957 G_CALLBACK(modest_msg_view_window_on_row_inserted),
959 priv->rows_reordered_handler =
960 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
961 G_CALLBACK(modest_msg_view_window_on_row_reordered),
964 if (header_view != NULL){
965 modest_header_view_add_observer(header_view,
966 MODEST_HEADER_VIEW_OBSERVER(window));
969 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
970 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
971 /* gtk_widget_show_all (GTK_WIDGET (window)); */
972 modest_msg_view_window_update_priority (window);
974 /* Check dimming rules */
975 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
976 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
977 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
979 return MODEST_WINDOW(window);
983 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
984 const gchar *modest_account_name,
985 const gchar *msg_uid)
987 ModestMsgViewWindow *window = NULL;
988 ModestMsgViewWindowPrivate *priv = NULL;
989 ModestWindowMgr *mgr = NULL;
991 mgr = modest_runtime_get_window_mgr ();
992 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
993 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
994 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
996 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
998 /* Remember that this is a search result,
999 * so we can disable some UI appropriately: */
1000 priv->is_search_result = TRUE;
1002 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1004 update_window_title (window);
1005 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1006 modest_msg_view_window_update_priority (window);
1008 /* Check dimming rules */
1009 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1010 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1011 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1013 return MODEST_WINDOW(window);
1017 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1018 const gchar *modest_account_name,
1019 const gchar *msg_uid)
1021 GObject *obj = NULL;
1022 ModestMsgViewWindowPrivate *priv;
1023 ModestWindowMgr *mgr = NULL;
1025 g_return_val_if_fail (msg, NULL);
1026 mgr = modest_runtime_get_window_mgr ();
1027 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1028 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1029 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1030 modest_account_name, msg_uid);
1032 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1033 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1035 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1037 /* Check dimming rules */
1038 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1039 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1040 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1042 return MODEST_WINDOW(obj);
1046 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1049 ModestMsgViewWindow *window)
1051 check_dimming_rules_after_change (window);
1055 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1057 ModestMsgViewWindow *window)
1059 check_dimming_rules_after_change (window);
1061 /* The window could have dissapeared */
1064 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1066 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1067 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1071 /* On insertions we check if the folder still has the message we are
1072 * showing or do not. If do not, we do nothing. Which means we are still
1073 * not attached to any header folder and thus next/prev buttons are
1074 * still dimmed. Once the message that is shown by msg-view is found, the
1075 * new model of header-view will be attached and the references will be set.
1076 * On each further insertions dimming rules will be checked. However
1077 * this requires extra CPU time at least works.
1078 * (An message might be deleted from TnyFolder and thus will not be
1079 * inserted into the model again for example if it is removed by the
1080 * imap server and the header view is refreshed.)
1083 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1084 GtkTreePath *tree_path,
1085 GtkTreeIter *tree_iter,
1086 ModestMsgViewWindow *window)
1088 ModestMsgViewWindowPrivate *priv = NULL;
1089 TnyHeader *header = NULL;
1091 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1092 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1094 g_assert (model == priv->header_model);
1096 /* Check if the newly inserted message is the same we are actually
1097 * showing. IF not, we should remain detached from the header model
1098 * and thus prev and next toolbar buttons should remain dimmed. */
1099 gtk_tree_model_get (model, tree_iter,
1100 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1103 if (TNY_IS_HEADER (header)) {
1106 uid = modest_tny_folder_get_header_unique_id (header);
1107 if (!g_str_equal(priv->msg_uid, uid)) {
1108 check_dimming_rules_after_change (window);
1110 g_object_unref (G_OBJECT(header));
1114 g_object_unref(G_OBJECT(header));
1117 if (priv->row_reference) {
1118 gtk_tree_row_reference_free (priv->row_reference);
1121 /* Setup row_reference for the actual msg. */
1122 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1123 if (priv->row_reference == NULL) {
1124 g_warning("No reference for msg header item.");
1128 /* Now set up next_row_reference. */
1129 if (priv->next_row_reference) {
1130 gtk_tree_row_reference_free (priv->next_row_reference);
1133 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1134 select_next_valid_row (priv->header_model,
1135 &(priv->next_row_reference), FALSE, priv->is_outbox);
1137 /* Connect the remaining callbacks to become able to detect
1138 * changes in header-view. */
1139 priv->row_changed_handler =
1140 g_signal_connect (priv->header_model, "row-changed",
1141 G_CALLBACK (modest_msg_view_window_on_row_changed),
1143 priv->row_deleted_handler =
1144 g_signal_connect (priv->header_model, "row-deleted",
1145 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1147 priv->rows_reordered_handler =
1148 g_signal_connect (priv->header_model, "rows-reordered",
1149 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1152 check_dimming_rules_after_change (window);
1156 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1160 ModestMsgViewWindow *window)
1162 ModestMsgViewWindowPrivate *priv = NULL;
1163 gboolean already_changed = FALSE;
1165 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1167 /* If the current row was reordered select the proper next
1168 valid row. The same if the next row reference changes */
1169 if (priv->row_reference &&
1170 gtk_tree_row_reference_valid (priv->row_reference)) {
1172 path = gtk_tree_row_reference_get_path (priv->row_reference);
1173 if (gtk_tree_path_compare (path, arg1) == 0) {
1174 if (priv->next_row_reference) {
1175 gtk_tree_row_reference_free (priv->next_row_reference);
1177 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1178 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1179 already_changed = TRUE;
1181 gtk_tree_path_free (path);
1183 if (!already_changed &&
1184 priv->next_row_reference &&
1185 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1187 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1188 if (gtk_tree_path_compare (path, arg1) == 0) {
1189 if (priv->next_row_reference) {
1190 gtk_tree_row_reference_free (priv->next_row_reference);
1192 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1193 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1195 gtk_tree_path_free (path);
1197 check_dimming_rules_after_change (window);
1200 /* The modest_msg_view_window_update_model_replaced implements update
1201 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1202 * actually belongs to the header-view is the same as the TnyFolder of
1203 * the message of msg-view or not. If they are different, there is
1204 * nothing to do. If they are the same, then the model has replaced and
1205 * the reference in msg-view shall be replaced from the old model to
1206 * the new model. In this case the view will be detached from it's
1207 * header folder. From this point the next/prev buttons are dimmed.
1210 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1211 GtkTreeModel *model,
1212 const gchar *tny_folder_id)
1214 ModestMsgViewWindowPrivate *priv = NULL;
1215 ModestMsgViewWindow *window = NULL;
1217 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1218 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1220 window = MODEST_MSG_VIEW_WINDOW(observer);
1221 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1223 /* If there is an other folder in the header-view then we do
1224 * not care about it's model (msg list). Else if the
1225 * header-view shows the folder the msg shown by us is in, we
1226 * shall replace our model reference and make some check. */
1227 if(model == NULL || tny_folder_id == NULL ||
1228 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1231 /* Model is changed(replaced), so we should forget the old
1232 * one. Because there might be other references and there
1233 * might be some change on the model even if we unreferenced
1234 * it, we need to disconnect our signals here. */
1235 if (priv->header_model) {
1236 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1237 priv->row_changed_handler))
1238 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1239 priv->row_changed_handler);
1240 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1241 priv->row_deleted_handler))
1242 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1243 priv->row_deleted_handler);
1244 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1245 priv->row_inserted_handler))
1246 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1247 priv->row_inserted_handler);
1248 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1249 priv->rows_reordered_handler))
1250 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1251 priv->rows_reordered_handler);
1254 if (priv->row_reference)
1255 gtk_tree_row_reference_free (priv->row_reference);
1256 if (priv->next_row_reference)
1257 gtk_tree_row_reference_free (priv->next_row_reference);
1258 g_object_unref(priv->header_model);
1261 priv->row_changed_handler = 0;
1262 priv->row_deleted_handler = 0;
1263 priv->row_inserted_handler = 0;
1264 priv->rows_reordered_handler = 0;
1265 priv->next_row_reference = NULL;
1266 priv->row_reference = NULL;
1267 priv->header_model = NULL;
1270 priv->header_model = g_object_ref (model);
1272 /* Also we must connect to the new model for row insertions.
1273 * Only for insertions now. We will need other ones only after
1274 * the msg is show by msg-view is added to the new model. */
1275 priv->row_inserted_handler =
1276 g_signal_connect (priv->header_model, "row-inserted",
1277 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1280 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1281 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1285 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1287 ModestMsgViewWindowPrivate *priv= NULL;
1289 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1290 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1292 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1296 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1298 ModestMsgViewWindowPrivate *priv= NULL;
1300 TnyHeader *header = NULL;
1301 GtkTreePath *path = NULL;
1304 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1305 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1307 /* If the message was not obtained from a treemodel,
1308 * for instance if it was opened directly by the search UI:
1310 if (priv->header_model == NULL ||
1311 priv->row_reference == NULL ||
1312 !gtk_tree_row_reference_valid (priv->row_reference)) {
1313 msg = modest_msg_view_window_get_message (self);
1315 header = tny_msg_get_header (msg);
1316 g_object_unref (msg);
1321 /* Get iter of the currently selected message in the header view: */
1322 path = gtk_tree_row_reference_get_path (priv->row_reference);
1323 g_return_val_if_fail (path != NULL, NULL);
1324 gtk_tree_model_get_iter (priv->header_model,
1328 /* Get current message header */
1329 gtk_tree_model_get (priv->header_model, &iter,
1330 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1333 gtk_tree_path_free (path);
1338 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1340 ModestMsgViewWindowPrivate *priv;
1342 g_return_val_if_fail (self, NULL);
1344 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1346 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1350 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1352 ModestMsgViewWindowPrivate *priv;
1354 g_return_val_if_fail (self, NULL);
1356 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1358 return (const gchar*) priv->msg_uid;
1362 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1365 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1366 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1367 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1371 is_active = gtk_toggle_action_get_active (toggle);
1374 gtk_widget_show (priv->find_toolbar);
1375 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1377 gtk_widget_hide (priv->find_toolbar);
1378 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1381 /* update the toggle buttons status */
1382 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1383 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1387 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1388 ModestMsgViewWindow *obj)
1390 GtkToggleAction *toggle;
1391 ModestWindowPrivate *parent_priv;
1392 ModestMsgViewWindowPrivate *priv;
1394 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1395 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1397 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1398 gtk_toggle_action_set_active (toggle, FALSE);
1399 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1403 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1404 ModestMsgViewWindow *obj)
1406 gchar *current_search;
1407 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1409 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1410 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1414 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1416 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1417 g_free (current_search);
1418 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1422 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1424 g_free (priv->last_search);
1425 priv->last_search = g_strdup (current_search);
1426 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1429 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1430 g_free (priv->last_search);
1431 priv->last_search = NULL;
1433 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1434 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1437 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1438 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1439 g_free (priv->last_search);
1440 priv->last_search = NULL;
1442 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1443 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1447 g_free (current_search);
1452 modest_msg_view_window_get_zoom (ModestWindow *window)
1454 ModestMsgViewWindowPrivate *priv;
1456 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1458 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1459 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1463 modest_msg_view_window_key_event (GtkWidget *window,
1469 focus = gtk_window_get_focus (GTK_WINDOW (window));
1471 /* for the find toolbar case */
1472 if (focus && GTK_IS_ENTRY (focus)) {
1473 if (event->keyval == GDK_BackSpace) {
1475 copy = gdk_event_copy ((GdkEvent *) event);
1476 gtk_widget_event (focus, copy);
1477 gdk_event_free (copy);
1482 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1483 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1484 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1485 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1486 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1487 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1488 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1489 /* gboolean return_value; */
1491 if (event->type == GDK_KEY_PRESS) {
1492 GtkScrollType scroll_type;
1494 switch (event->keyval) {
1497 scroll_type = GTK_SCROLL_STEP_UP; break;
1500 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1502 case GDK_KP_Page_Up:
1503 scroll_type = GTK_SCROLL_PAGE_UP; break;
1505 case GDK_KP_Page_Down:
1506 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1509 scroll_type = GTK_SCROLL_START; break;
1512 scroll_type = GTK_SCROLL_END; break;
1513 default: scroll_type = GTK_SCROLL_NONE;
1516 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1517 /* scroll_type, FALSE, &return_value); */
1528 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1531 ModestMsgViewWindowPrivate *priv;
1532 GtkTreeIter tmp_iter;
1533 gboolean is_last_selected;
1535 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1536 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1538 /*if no model (so no rows at all), then virtually we are the last*/
1539 if (!priv->header_model || !priv->row_reference)
1542 if (!gtk_tree_row_reference_valid (priv->row_reference))
1545 path = gtk_tree_row_reference_get_path (priv->row_reference);
1549 is_last_selected = TRUE;
1550 while (is_last_selected) {
1552 gtk_tree_path_next (path);
1553 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1555 gtk_tree_model_get (priv->header_model, &tmp_iter,
1556 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1559 if (msg_is_visible (header, priv->is_outbox))
1560 is_last_selected = FALSE;
1561 g_object_unref(G_OBJECT(header));
1564 gtk_tree_path_free (path);
1565 return is_last_selected;
1569 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1571 ModestMsgViewWindowPrivate *priv;
1573 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1574 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1576 return priv->header_model != NULL;
1580 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1582 ModestMsgViewWindowPrivate *priv;
1584 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1585 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1587 return priv->is_search_result;
1591 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1593 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1595 if (!check_outbox) {
1598 ModestTnySendQueueStatus status;
1599 status = modest_tny_all_send_queues_get_msg_status (header);
1600 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1601 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1606 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1609 ModestMsgViewWindowPrivate *priv;
1610 gboolean is_first_selected;
1611 GtkTreeIter tmp_iter;
1613 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1614 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1616 /*if no model (so no rows at all), then virtually we are the first*/
1617 if (!priv->header_model || !priv->row_reference)
1620 if (!gtk_tree_row_reference_valid (priv->row_reference))
1623 path = gtk_tree_row_reference_get_path (priv->row_reference);
1627 is_first_selected = TRUE;
1628 while (is_first_selected) {
1630 if(!gtk_tree_path_prev (path))
1632 /* Here the 'if' is needless for logic, but let make sure
1633 * iter is valid for gtk_tree_model_get. */
1634 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1636 gtk_tree_model_get (priv->header_model, &tmp_iter,
1637 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1640 if (msg_is_visible (header, priv->is_outbox))
1641 is_first_selected = FALSE;
1642 g_object_unref(G_OBJECT(header));
1645 gtk_tree_path_free (path);
1646 return is_first_selected;
1651 GtkTreeRowReference *row_reference;
1655 message_reader_performer (gboolean canceled,
1657 GtkWindow *parent_window,
1658 TnyAccount *account,
1661 ModestMailOperation *mail_op = NULL;
1662 MsgReaderInfo *info;
1664 info = (MsgReaderInfo *) user_data;
1665 if (canceled || err) {
1669 /* Register the header - it'll be unregistered in the callback */
1670 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1672 /* New mail operation */
1673 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1674 modest_ui_actions_disk_operations_error_handler,
1677 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1678 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1679 g_object_unref (mail_op);
1681 /* Update dimming rules */
1682 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1683 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1686 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1687 g_object_unref (info->header);
1688 g_slice_free (MsgReaderInfo, info);
1693 * Reads the message whose summary item is @header. It takes care of
1694 * several things, among others:
1696 * If the message was not previously downloaded then ask the user
1697 * before downloading. If there is no connection launch the connection
1698 * dialog. Update toolbar dimming rules.
1700 * Returns: TRUE if the mail operation was started, otherwise if the
1701 * user do not want to download the message, or if the user do not
1702 * want to connect, then the operation is not issued
1705 message_reader (ModestMsgViewWindow *window,
1706 ModestMsgViewWindowPrivate *priv,
1708 GtkTreeRowReference *row_reference)
1710 ModestWindowMgr *mgr;
1711 TnyAccount *account;
1713 MsgReaderInfo *info;
1715 g_return_val_if_fail (row_reference != NULL, FALSE);
1717 mgr = modest_runtime_get_window_mgr ();
1718 /* Msg download completed */
1719 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1720 /* Ask the user if he wants to download the message if
1722 if (!tny_device_is_online (modest_runtime_get_device())) {
1723 GtkResponseType response;
1725 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1726 _("mcen_nc_get_msg"));
1727 if (response == GTK_RESPONSE_CANCEL)
1730 folder = tny_header_get_folder (header);
1731 info = g_slice_new (MsgReaderInfo);
1732 info->header = g_object_ref (header);
1733 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1735 /* Offer the connection dialog if necessary */
1736 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1738 TNY_FOLDER_STORE (folder),
1739 message_reader_performer,
1741 g_object_unref (folder);
1746 folder = tny_header_get_folder (header);
1747 account = tny_folder_get_account (folder);
1748 info = g_slice_new (MsgReaderInfo);
1749 info->header = g_object_ref (header);
1750 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1752 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1753 g_object_unref (account);
1754 g_object_unref (folder);
1760 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1762 ModestMsgViewWindowPrivate *priv;
1763 GtkTreePath *path= NULL;
1764 GtkTreeIter tmp_iter;
1766 gboolean retval = TRUE;
1767 GtkTreeRowReference *row_reference = NULL;
1769 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1770 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1772 if (!priv->row_reference)
1775 /* Update the next row reference if it's not valid. This could
1776 happen if for example the header which it was pointing to,
1777 was deleted. The best place to do it is in the row-deleted
1778 handler but the tinymail model do not work like the glib
1779 tree models and reports the deletion when the row is still
1781 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1782 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1783 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1784 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1787 if (priv->next_row_reference)
1788 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1792 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1794 gtk_tree_model_get_iter (priv->header_model,
1797 gtk_tree_path_free (path);
1799 gtk_tree_model_get (priv->header_model, &tmp_iter,
1800 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1803 /* Read the message & show it */
1804 if (!message_reader (window, priv, header, row_reference)) {
1807 gtk_tree_row_reference_free (row_reference);
1810 g_object_unref (header);
1816 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1818 ModestMsgViewWindowPrivate *priv = NULL;
1820 gboolean finished = FALSE;
1821 gboolean retval = FALSE;
1823 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1824 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1826 /* Return inmediatly if there is no header model */
1827 if (!priv->header_model || !priv->row_reference)
1830 path = gtk_tree_row_reference_get_path (priv->row_reference);
1831 while (!finished && gtk_tree_path_prev (path)) {
1835 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1836 gtk_tree_model_get (priv->header_model, &iter,
1837 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1841 if (msg_is_visible (header, priv->is_outbox)) {
1842 GtkTreeRowReference *row_reference;
1843 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1844 /* Read the message & show it */
1845 retval = message_reader (window, priv, header, row_reference);
1846 gtk_tree_row_reference_free (row_reference);
1850 g_object_unref (header);
1854 gtk_tree_path_free (path);
1859 view_msg_cb (ModestMailOperation *mail_op,
1866 ModestMsgViewWindow *self = NULL;
1867 ModestMsgViewWindowPrivate *priv = NULL;
1868 GtkTreeRowReference *row_reference = NULL;
1870 /* Unregister the header (it was registered before creating the mail operation) */
1871 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1873 row_reference = (GtkTreeRowReference *) user_data;
1875 gtk_tree_row_reference_free (row_reference);
1879 /* If there was any error */
1880 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1881 gtk_tree_row_reference_free (row_reference);
1885 /* Get the window */
1886 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1887 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1888 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1890 /* Update the row reference */
1891 if (priv->row_reference != NULL) {
1892 gtk_tree_row_reference_free (priv->row_reference);
1893 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1894 if (priv->next_row_reference != NULL) {
1895 gtk_tree_row_reference_free (priv->next_row_reference);
1897 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1898 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1901 /* Mark header as read */
1902 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1903 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1905 /* Set new message */
1906 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1907 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1908 modest_msg_view_window_update_priority (self);
1909 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1910 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1913 /* Set the new message uid of the window */
1914 if (priv->msg_uid) {
1915 g_free (priv->msg_uid);
1916 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1919 /* Notify the observers */
1920 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1921 0, priv->header_model, priv->row_reference);
1924 g_object_unref (self);
1925 gtk_tree_row_reference_free (row_reference);
1929 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1931 ModestMsgViewWindowPrivate *priv;
1933 TnyFolderType folder_type;
1935 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1937 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1939 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1943 folder = tny_msg_get_folder (msg);
1945 folder_type = modest_tny_folder_guess_folder_type (folder);
1946 g_object_unref (folder);
1948 g_object_unref (msg);
1956 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1958 ModestMsgViewWindowPrivate *priv;
1959 TnyHeader *header = NULL;
1960 TnyHeaderFlags flags = 0;
1962 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1964 if (priv->header_model && priv->row_reference) {
1966 GtkTreePath *path = NULL;
1968 path = gtk_tree_row_reference_get_path (priv->row_reference);
1969 g_return_if_fail (path != NULL);
1970 gtk_tree_model_get_iter (priv->header_model,
1972 gtk_tree_row_reference_get_path (priv->row_reference));
1974 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1976 gtk_tree_path_free (path);
1979 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1981 header = tny_msg_get_header (msg);
1982 g_object_unref (msg);
1987 flags = tny_header_get_flags (header);
1988 g_object_unref(G_OBJECT(header));
1991 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1996 toolbar_resize (ModestMsgViewWindow *self)
1998 ModestMsgViewWindowPrivate *priv = NULL;
1999 ModestWindowPrivate *parent_priv = NULL;
2001 gint static_button_size;
2002 ModestWindowMgr *mgr;
2004 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2005 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2006 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2008 mgr = modest_runtime_get_window_mgr ();
2009 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2011 if (parent_priv->toolbar) {
2012 /* left size buttons */
2013 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2014 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2015 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2016 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2017 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2018 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2019 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2020 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2021 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2022 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2023 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2024 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2025 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2026 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2027 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2028 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2030 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2031 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2032 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2033 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2034 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2035 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2036 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2037 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2043 modest_msg_view_window_show_toolbar (ModestWindow *self,
2044 gboolean show_toolbar)
2046 ModestMsgViewWindowPrivate *priv = NULL;
2047 ModestWindowPrivate *parent_priv;
2048 GtkWidget *reply_button = NULL, *menu = NULL;
2049 GtkWidget *placeholder = NULL;
2052 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2053 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2055 /* Set optimized view status */
2056 priv->optimized_view = !show_toolbar;
2058 if (!parent_priv->toolbar) {
2059 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2061 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2063 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2064 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2065 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2066 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2067 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2069 /* Add ProgressBar (Transfer toolbar) */
2070 priv->progress_bar = modest_progress_bar_new ();
2071 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2072 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2073 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2074 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2075 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2077 /* Connect cancel 'clicked' signal to abort progress mode */
2078 g_signal_connect(priv->cancel_toolitem, "clicked",
2079 G_CALLBACK(cancel_progressbar),
2082 /* Add it to the observers list */
2083 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2086 hildon_window_add_toolbar (HILDON_WINDOW (self),
2087 GTK_TOOLBAR (parent_priv->toolbar));
2089 /* Set reply button tap and hold menu */
2090 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2091 "/ToolBar/ToolbarMessageReply");
2092 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2093 "/ToolbarReplyCSM");
2094 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2098 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2099 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2100 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2102 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2103 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2104 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2106 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2109 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2110 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2115 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2117 ModestMsgViewWindow *window)
2119 if (!GTK_WIDGET_VISIBLE (window))
2122 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2126 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2128 ModestMsgViewWindowPrivate *priv;
2130 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2131 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2133 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2137 cancel_progressbar (GtkToolButton *toolbutton,
2138 ModestMsgViewWindow *self)
2141 ModestMsgViewWindowPrivate *priv;
2143 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2145 /* Get operation observers and cancel its current operation */
2146 tmp = priv->progress_widgets;
2148 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2149 tmp=g_slist_next(tmp);
2153 observers_empty (ModestMsgViewWindow *self)
2156 ModestMsgViewWindowPrivate *priv;
2157 gboolean is_empty = TRUE;
2158 guint pending_ops = 0;
2160 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2161 tmp = priv->progress_widgets;
2163 /* Check all observers */
2164 while (tmp && is_empty) {
2165 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2166 is_empty = pending_ops == 0;
2168 tmp = g_slist_next(tmp);
2175 on_account_removed (TnyAccountStore *account_store,
2176 TnyAccount *account,
2179 /* Do nothing if it's a transport account, because we only
2180 show the messages of a store account */
2181 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2182 const gchar *parent_acc = NULL;
2183 const gchar *our_acc = NULL;
2185 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2186 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2188 /* Close this window if I'm showing a message of the removed account */
2189 if (strcmp (parent_acc, our_acc) == 0)
2190 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2195 on_mail_operation_started (ModestMailOperation *mail_op,
2198 ModestMsgViewWindow *self;
2199 ModestMailOperationTypeOperation op_type;
2201 ModestMsgViewWindowPrivate *priv;
2202 GObject *source = NULL;
2204 self = MODEST_MSG_VIEW_WINDOW (user_data);
2205 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2206 op_type = modest_mail_operation_get_type_operation (mail_op);
2207 tmp = priv->progress_widgets;
2208 source = modest_mail_operation_get_source(mail_op);
2209 if (G_OBJECT (self) == source) {
2210 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2211 set_toolbar_transfer_mode(self);
2213 modest_progress_object_add_operation (
2214 MODEST_PROGRESS_OBJECT (tmp->data),
2216 tmp = g_slist_next (tmp);
2220 g_object_unref (source);
2224 on_mail_operation_finished (ModestMailOperation *mail_op,
2227 ModestMsgViewWindow *self;
2228 ModestMailOperationTypeOperation op_type;
2230 ModestMsgViewWindowPrivate *priv;
2232 self = MODEST_MSG_VIEW_WINDOW (user_data);
2233 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2234 op_type = modest_mail_operation_get_type_operation (mail_op);
2235 tmp = priv->progress_widgets;
2237 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2239 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2241 tmp = g_slist_next (tmp);
2244 /* If no more operations are being observed, NORMAL mode is enabled again */
2245 if (observers_empty (self)) {
2246 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2249 /* Update dimming rules. We have to do this right here
2250 and not in view_msg_cb because at that point the
2251 transfer mode is still enabled so the dimming rule
2252 won't let the user delete the message that has been
2253 readed for example */
2254 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2255 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2260 on_queue_changed (ModestMailOperationQueue *queue,
2261 ModestMailOperation *mail_op,
2262 ModestMailOperationQueueNotification type,
2263 ModestMsgViewWindow *self)
2265 ModestMsgViewWindowPrivate *priv;
2267 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2269 /* If this operations was created by another window, do nothing */
2270 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2273 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2274 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2276 "operation-started",
2277 G_CALLBACK (on_mail_operation_started),
2279 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2281 "operation-finished",
2282 G_CALLBACK (on_mail_operation_finished),
2284 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2285 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2287 "operation-started");
2288 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2290 "operation-finished");
2295 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2297 ModestMsgViewWindowPrivate *priv;
2298 TnyList *selected_attachments = NULL;
2300 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2301 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2303 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2305 return selected_attachments;
2311 guint banner_idle_id;
2312 } DecodeAsyncHelper;
2315 decode_async_banner_idle (gpointer user_data)
2317 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2319 helper->banner_idle_id = 0;
2320 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2321 g_object_ref (helper->banner);
2327 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2333 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2335 if (helper->banner_idle_id > 0) {
2336 g_source_remove (helper->banner_idle_id);
2337 helper->banner_idle_id = 0;
2339 if (helper->banner) {
2340 gtk_widget_destroy (helper->banner);
2342 if (cancelled || err) {
2343 modest_platform_information_banner (NULL, NULL,
2344 _("mail_ib_file_operation_failed"));
2348 /* make the file read-only */
2349 g_chmod(helper->filepath, 0444);
2351 /* Activate the file */
2352 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2356 g_free (helper->filepath);
2357 g_object_unref (helper->banner);
2358 g_slice_free (DecodeAsyncHelper, helper);
2362 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2363 TnyMimePart *mime_part)
2365 ModestMsgViewWindowPrivate *priv;
2366 const gchar *msg_uid;
2367 gchar *attachment_uid = NULL;
2368 gint attachment_index = 0;
2369 TnyList *attachments;
2371 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2372 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2373 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2375 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2376 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2377 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2378 g_object_unref (attachments);
2380 if (msg_uid && attachment_index >= 0) {
2381 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2384 if (mime_part == NULL) {
2385 gboolean error = FALSE;
2386 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2387 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2389 } else if (tny_list_get_length (selected_attachments) > 1) {
2390 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2394 iter = tny_list_create_iterator (selected_attachments);
2395 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2396 g_object_unref (iter);
2398 g_object_unref (selected_attachments);
2403 g_object_ref (mime_part);
2406 if (tny_mime_part_is_purged (mime_part)) {
2407 g_object_unref (mime_part);
2411 if (!modest_tny_mime_part_is_msg (mime_part)) {
2412 gchar *filepath = NULL;
2413 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2414 gboolean show_error_banner = FALSE;
2415 TnyFsStream *temp_stream = NULL;
2416 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2419 if (temp_stream != NULL) {
2420 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2421 helper->filepath = g_strdup (filepath);
2422 helper->banner = NULL;
2423 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2424 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2425 on_decode_to_stream_async_handler,
2428 g_object_unref (temp_stream);
2429 /* NOTE: files in the temporary area will be automatically
2430 * cleaned after some time if they are no longer in use */
2433 const gchar *content_type;
2434 /* the file may already exist but it isn't writable,
2435 * let's try to open it anyway */
2436 content_type = tny_mime_part_get_content_type (mime_part);
2437 modest_platform_activate_file (filepath, content_type);
2439 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2440 show_error_banner = TRUE;
2445 if (show_error_banner)
2446 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2448 /* message attachment */
2449 TnyHeader *header = NULL;
2450 ModestWindowMgr *mgr;
2451 ModestWindow *msg_win = NULL;
2454 header = tny_msg_get_header (TNY_MSG (mime_part));
2455 mgr = modest_runtime_get_window_mgr ();
2456 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2459 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2460 * thus, we don't do anything */
2461 g_warning ("window for is already being created");
2463 /* it's not found, so create a new window for it */
2464 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2465 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2467 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2468 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2469 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2470 modest_window_get_zoom (MODEST_WINDOW (window)));
2471 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2472 gtk_widget_show_all (GTK_WIDGET (msg_win));
2475 g_object_unref (mime_part);
2488 GnomeVFSResult result;
2491 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2492 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2493 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2494 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2497 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2501 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2502 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2503 g_free (pair->filename);
2504 g_object_unref (pair->part);
2505 g_slice_free (SaveMimePartPair, pair);
2507 g_list_free (info->pairs);
2510 gtk_widget_destroy (info->banner);
2511 g_slice_free (SaveMimePartInfo, info);
2516 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2518 if (info->pairs != NULL) {
2519 save_mime_part_to_file (info);
2521 /* This is a GDK lock because we are an idle callback and
2522 * hildon_banner_show_information is or does Gtk+ code */
2524 gdk_threads_enter (); /* CHECKED */
2525 save_mime_part_info_free (info, TRUE);
2526 if (info->result == GNOME_VFS_OK) {
2527 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2528 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2529 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2530 "cerm_device_memory_full"));
2532 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2534 gdk_threads_leave (); /* CHECKED */
2541 save_mime_part_to_file (SaveMimePartInfo *info)
2543 GnomeVFSHandle *handle;
2545 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2547 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2548 if (info->result == GNOME_VFS_OK) {
2549 GError *error = NULL;
2550 stream = tny_vfs_stream_new (handle);
2551 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2552 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2554 info->result = GNOME_VFS_ERROR_IO;
2556 g_object_unref (G_OBJECT (stream));
2557 g_object_unref (pair->part);
2558 g_slice_free (SaveMimePartPair, pair);
2559 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2561 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2562 save_mime_part_info_free (info, FALSE);
2565 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2570 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2572 gboolean is_ok = TRUE;
2573 gint replaced_files = 0;
2574 const GList *files = info->pairs;
2577 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2578 SaveMimePartPair *pair = iter->data;
2579 if (modest_utils_file_exists (pair->filename)) {
2583 if (replaced_files) {
2584 GtkWidget *confirm_overwrite_dialog;
2585 const gchar *message = (replaced_files == 1) ?
2586 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2587 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2588 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2591 gtk_widget_destroy (confirm_overwrite_dialog);
2595 save_mime_part_info_free (info, TRUE);
2597 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2598 _CS("sfil_ib_saving"));
2599 info->banner = banner;
2600 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2606 save_attachments_response (GtkDialog *dialog,
2610 TnyList *mime_parts;
2612 GList *files_to_save = NULL;
2614 mime_parts = TNY_LIST (user_data);
2616 if (arg1 != GTK_RESPONSE_OK)
2619 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2621 if (!modest_utils_folder_writable (chooser_uri)) {
2622 hildon_banner_show_information
2623 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2627 iter = tny_list_create_iterator (mime_parts);
2628 while (!tny_iterator_is_done (iter)) {
2629 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2631 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2632 !tny_mime_part_is_purged (mime_part) &&
2633 (tny_mime_part_get_filename (mime_part) != NULL)) {
2634 SaveMimePartPair *pair;
2636 pair = g_slice_new0 (SaveMimePartPair);
2638 if (tny_list_get_length (mime_parts) > 1) {
2640 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2641 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2644 pair->filename = g_strdup (chooser_uri);
2646 pair->part = mime_part;
2647 files_to_save = g_list_prepend (files_to_save, pair);
2649 tny_iterator_next (iter);
2651 g_object_unref (iter);
2653 g_free (chooser_uri);
2655 if (files_to_save != NULL) {
2656 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2657 info->pairs = files_to_save;
2658 info->result = TRUE;
2659 save_mime_parts_to_file_with_checks (info);
2663 /* Free and close the dialog */
2664 g_object_unref (mime_parts);
2665 gtk_widget_destroy (GTK_WIDGET (dialog));
2669 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2671 ModestMsgViewWindowPrivate *priv;
2672 GtkWidget *save_dialog = NULL;
2673 gchar *folder = NULL;
2674 gchar *filename = NULL;
2675 gchar *save_multiple_str = NULL;
2677 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2678 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2680 if (mime_parts == NULL) {
2681 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2682 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2685 g_object_ref (mime_parts);
2688 /* prepare dialog */
2689 if (tny_list_get_length (mime_parts) == 1) {
2691 /* only one attachment selected */
2692 iter = tny_list_create_iterator (mime_parts);
2693 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2694 g_object_unref (iter);
2695 if (!modest_tny_mime_part_is_msg (mime_part) &&
2696 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2697 !tny_mime_part_is_purged (mime_part)) {
2698 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2700 /* TODO: show any error? */
2701 g_warning ("Tried to save a non-file attachment");
2702 g_object_unref (mime_parts);
2705 g_object_unref (mime_part);
2707 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2708 tny_list_get_length (mime_parts));
2711 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2712 GTK_FILE_CHOOSER_ACTION_SAVE);
2715 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2716 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2721 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2726 /* if multiple, set multiple string */
2727 if (save_multiple_str) {
2728 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2729 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2732 /* We must run this asynchronously, because the hildon dialog
2733 performs a gtk_dialog_run by itself which leads to gdk
2735 g_signal_connect (save_dialog, "response",
2736 G_CALLBACK (save_attachments_response), mime_parts);
2738 gtk_widget_show_all (save_dialog);
2742 show_remove_attachment_information (gpointer userdata)
2744 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2745 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2747 /* We're outside the main lock */
2748 gdk_threads_enter ();
2750 if (priv->remove_attachment_banner != NULL) {
2751 gtk_widget_destroy (priv->remove_attachment_banner);
2752 g_object_unref (priv->remove_attachment_banner);
2755 priv->remove_attachment_banner = g_object_ref (
2756 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2758 gdk_threads_leave ();
2764 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2766 ModestMsgViewWindowPrivate *priv;
2767 TnyList *mime_parts = NULL;
2768 gchar *confirmation_message;
2774 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2775 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2778 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2780 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2782 /* Remove already purged messages from mime parts list */
2783 iter = tny_list_create_iterator (mime_parts);
2784 while (!tny_iterator_is_done (iter)) {
2785 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2786 tny_iterator_next (iter);
2787 if (tny_mime_part_is_purged (part)) {
2788 tny_list_remove (mime_parts, (GObject *) part);
2790 g_object_unref (part);
2792 g_object_unref (iter);
2794 if (tny_list_get_length (mime_parts) == 0) {
2795 g_object_unref (mime_parts);
2799 n_attachments = tny_list_get_length (mime_parts);
2800 if (n_attachments == 1) {
2804 iter = tny_list_create_iterator (mime_parts);
2805 part = (TnyMimePart *) tny_iterator_get_current (iter);
2806 g_object_unref (iter);
2807 if (modest_tny_mime_part_is_msg (part)) {
2809 header = tny_msg_get_header (TNY_MSG (part));
2810 filename = tny_header_dup_subject (header);
2811 g_object_unref (header);
2812 if (filename == NULL)
2813 filename = g_strdup (_("mail_va_no_subject"));
2815 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2817 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2819 g_object_unref (part);
2821 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2822 "mcen_nc_purge_files_text",
2823 n_attachments), n_attachments);
2825 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2826 confirmation_message);
2827 g_free (confirmation_message);
2829 if (response != GTK_RESPONSE_OK) {
2830 g_object_unref (mime_parts);
2834 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2836 iter = tny_list_create_iterator (mime_parts);
2837 while (!tny_iterator_is_done (iter)) {
2840 part = (TnyMimePart *) tny_iterator_get_current (iter);
2841 tny_mime_part_set_purged (TNY_MIME_PART (part));
2842 g_object_unref (part);
2843 tny_iterator_next (iter);
2845 g_object_unref (iter);
2847 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2848 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2849 tny_msg_rewrite_cache (msg);
2850 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2851 g_object_unref (msg);
2853 g_object_unref (mime_parts);
2855 if (priv->purge_timeout > 0) {
2856 g_source_remove (priv->purge_timeout);
2857 priv->purge_timeout = 0;
2860 if (priv->remove_attachment_banner) {
2861 gtk_widget_destroy (priv->remove_attachment_banner);
2862 g_object_unref (priv->remove_attachment_banner);
2863 priv->remove_attachment_banner = NULL;
2871 update_window_title (ModestMsgViewWindow *window)
2873 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2875 TnyHeader *header = NULL;
2876 gchar *subject = NULL;
2878 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2881 header = tny_msg_get_header (msg);
2882 subject = tny_header_dup_subject (header);
2883 g_object_unref (header);
2884 g_object_unref (msg);
2887 if ((subject == NULL)||(subject[0] == '\0')) {
2889 subject = g_strdup (_("mail_va_no_subject"));
2892 gtk_window_set_title (GTK_WINDOW (window), subject);
2896 static void on_move_focus (GtkWidget *widget,
2897 GtkDirectionType direction,
2900 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
2904 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
2906 GnomeVFSResult result;
2907 GnomeVFSHandle *handle = NULL;
2908 GnomeVFSFileInfo *info = NULL;
2911 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2912 if (result != GNOME_VFS_OK) {
2917 info = gnome_vfs_file_info_new ();
2918 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
2919 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
2920 /* We put a "safe" default size for going to cache */
2921 *expected_size = (300*1024);
2923 *expected_size = info->size;
2925 gnome_vfs_file_info_unref (info);
2927 stream = tny_vfs_stream_new (handle);
2936 TnyStream *output_stream;
2937 GtkWidget *msg_view;
2941 on_fetch_image_idle_refresh_view (gpointer userdata)
2944 FetchImageData *fidata = (FetchImageData *) userdata;
2945 g_message ("REFRESH VIEW");
2946 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
2947 g_message ("QUEUING DRAW");
2948 gtk_widget_queue_draw (fidata->msg_view);
2950 g_object_unref (fidata->msg_view);
2951 g_slice_free (FetchImageData, fidata);
2956 on_fetch_image_thread (gpointer userdata)
2958 FetchImageData *fidata = (FetchImageData *) userdata;
2959 TnyStreamCache *cache;
2960 TnyStream *cache_stream;
2962 cache = modest_runtime_get_images_cache ();
2963 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
2964 g_free (fidata->cache_id);
2965 g_free (fidata->uri);
2967 if (cache_stream != NULL) {
2968 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
2969 tny_stream_close (cache_stream);
2970 g_object_unref (cache_stream);
2973 tny_stream_close (fidata->output_stream);
2974 g_object_unref (fidata->output_stream);
2977 gdk_threads_enter ();
2978 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
2979 gdk_threads_leave ();
2985 on_fetch_image (ModestMsgView *msgview,
2988 ModestMsgViewWindow *window)
2990 const gchar *current_account;
2991 ModestMsgViewWindowPrivate *priv;
2992 FetchImageData *fidata;
2994 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2996 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
2998 fidata = g_slice_new0 (FetchImageData);
2999 fidata->msg_view = g_object_ref (msgview);
3000 fidata->uri = g_strdup (uri);
3001 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3002 fidata->output_stream = g_object_ref (stream);
3004 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3005 g_object_unref (fidata->output_stream);
3006 g_free (fidata->cache_id);
3007 g_free (fidata->uri);
3008 g_object_unref (fidata->msg_view);
3009 g_slice_free (FetchImageData, fidata);
3010 tny_stream_close (stream);