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 <hildon/hildon-picker-dialog.h>
53 #include "modest-defs.h"
54 #include "modest-hildon-includes.h"
55 #include "modest-ui-dimming-manager.h"
56 #include <gdk/gdkkeysyms.h>
57 #include <modest-tny-account.h>
58 #include <modest-mime-part-view.h>
59 #include <modest-isearch-view.h>
60 #include <modest-tny-mime-part.h>
61 #include <modest-address-book.h>
64 #include <glib/gstdio.h>
65 #include <modest-debug.h>
67 #define MYDOCS_ENV "MYDOCSDIR"
68 #define DOCS_FOLDER ".documents"
70 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
71 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
72 static void modest_header_view_observer_init(
73 ModestHeaderViewObserverIface *iface_class);
74 static void modest_msg_view_window_finalize (GObject *obj);
75 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
77 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
78 ModestMsgViewWindow *obj);
79 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
80 ModestMsgViewWindow *obj);
82 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
84 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
85 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
89 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
91 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
92 gboolean show_toolbar);
94 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
96 ModestMsgViewWindow *window);
98 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
101 ModestMsgViewWindow *window);
103 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
105 ModestMsgViewWindow *window);
107 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
108 GtkTreePath *tree_path,
109 GtkTreeIter *tree_iter,
110 ModestMsgViewWindow *window);
112 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
116 ModestMsgViewWindow *window);
118 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
120 const gchar *tny_folder_id);
122 static void cancel_progressbar (GtkToolButton *toolbutton,
123 ModestMsgViewWindow *self);
125 static void on_queue_changed (ModestMailOperationQueue *queue,
126 ModestMailOperation *mail_op,
127 ModestMailOperationQueueNotification type,
128 ModestMsgViewWindow *self);
130 static void on_account_removed (TnyAccountStore *account_store,
134 static void on_move_focus (GtkWidget *widget,
135 GtkDirectionType direction,
138 static void view_msg_cb (ModestMailOperation *mail_op,
145 static void set_toolbar_mode (ModestMsgViewWindow *self,
146 ModestToolBarModes mode);
148 static void update_window_title (ModestMsgViewWindow *window);
150 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
151 static void init_window (ModestMsgViewWindow *obj);
153 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
155 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
157 static gboolean on_fetch_image (ModestMsgView *msgview,
160 ModestMsgViewWindow *window);
162 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
163 GtkScrollType scroll_type,
167 /* list my signals */
174 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
175 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
178 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
179 struct _ModestMsgViewWindowPrivate {
182 GtkWidget *main_scroll;
183 GtkWidget *find_toolbar;
186 /* Progress observers */
187 GtkWidget *progress_bar;
188 GSList *progress_widgets;
191 GtkWidget *progress_toolitem;
192 GtkWidget *cancel_toolitem;
193 GtkWidget *prev_toolitem;
194 GtkWidget *next_toolitem;
195 ModestToolBarModes current_toolbar_mode;
197 /* Optimized view enabled */
198 gboolean optimized_view;
200 /* Whether this was created via the *_new_for_search_result() function. */
201 gboolean is_search_result;
203 /* Whether the message is in outbox */
206 /* A reference to the @model of the header view
207 * to allow selecting previous/next messages,
208 * if the message is currently selected in the header view.
210 const gchar *header_folder_id;
211 GtkTreeModel *header_model;
212 GtkTreeRowReference *row_reference;
213 GtkTreeRowReference *next_row_reference;
215 gulong clipboard_change_handler;
216 gulong queue_change_handler;
217 gulong account_removed_handler;
218 gulong row_changed_handler;
219 gulong row_deleted_handler;
220 gulong row_inserted_handler;
221 gulong rows_reordered_handler;
224 GtkWidget *remove_attachment_banner;
226 guint progress_bar_timeout;
233 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
234 MODEST_TYPE_MSG_VIEW_WINDOW, \
235 ModestMsgViewWindowPrivate))
237 static GtkWindowClass *parent_class = NULL;
239 /* uncomment the following if you have defined any signals */
240 static guint signals[LAST_SIGNAL] = {0};
243 modest_msg_view_window_get_type (void)
245 static GType my_type = 0;
247 static const GTypeInfo my_info = {
248 sizeof(ModestMsgViewWindowClass),
249 NULL, /* base init */
250 NULL, /* base finalize */
251 (GClassInitFunc) modest_msg_view_window_class_init,
252 NULL, /* class finalize */
253 NULL, /* class data */
254 sizeof(ModestMsgViewWindow),
256 (GInstanceInitFunc) modest_msg_view_window_init,
259 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
260 "ModestMsgViewWindow",
263 static const GInterfaceInfo modest_header_view_observer_info =
265 (GInterfaceInitFunc) modest_header_view_observer_init,
266 NULL, /* interface_finalize */
267 NULL /* interface_data */
270 g_type_add_interface_static (my_type,
271 MODEST_TYPE_HEADER_VIEW_OBSERVER,
272 &modest_header_view_observer_info);
278 save_state (ModestWindow *self)
280 modest_widget_memory_save (modest_runtime_get_conf (),
282 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
286 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
287 GtkScrollType scroll_type,
291 ModestMsgViewWindowPrivate *priv;
292 gboolean return_value;
294 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
295 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
300 add_scroll_binding (GtkBindingSet *binding_set,
302 GtkScrollType scroll)
304 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
306 gtk_binding_entry_add_signal (binding_set, keyval, 0,
308 GTK_TYPE_SCROLL_TYPE, scroll,
309 G_TYPE_BOOLEAN, FALSE);
310 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
312 GTK_TYPE_SCROLL_TYPE, scroll,
313 G_TYPE_BOOLEAN, FALSE);
317 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
319 GObjectClass *gobject_class;
320 ModestWindowClass *modest_window_class;
321 GtkBindingSet *binding_set;
323 gobject_class = (GObjectClass*) klass;
324 modest_window_class = (ModestWindowClass *) klass;
326 parent_class = g_type_class_peek_parent (klass);
327 gobject_class->finalize = modest_msg_view_window_finalize;
329 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
330 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
331 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
333 modest_window_class->save_state_func = save_state;
335 klass->scroll_child = modest_msg_view_window_scroll_child;
337 signals[MSG_CHANGED_SIGNAL] =
338 g_signal_new ("msg-changed",
339 G_TYPE_FROM_CLASS (gobject_class),
341 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
343 modest_marshal_VOID__POINTER_POINTER,
344 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
346 signals[SCROLL_CHILD_SIGNAL] =
347 g_signal_new ("scroll-child",
348 G_TYPE_FROM_CLASS (gobject_class),
349 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
350 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
352 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
353 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
355 binding_set = gtk_binding_set_by_class (klass);
356 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
357 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
358 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
359 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
360 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
361 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
363 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
367 static void modest_header_view_observer_init(
368 ModestHeaderViewObserverIface *iface_class)
370 iface_class->update_func = modest_msg_view_window_update_model_replaced;
374 modest_msg_view_window_init (ModestMsgViewWindow *obj)
376 ModestMsgViewWindowPrivate *priv;
377 ModestWindowPrivate *parent_priv = NULL;
378 GtkActionGroup *action_group = NULL;
379 GError *error = NULL;
380 GdkPixbuf *window_icon;
382 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
383 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
384 parent_priv->ui_manager = gtk_ui_manager_new();
386 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
387 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
389 /* Add common actions */
390 gtk_action_group_add_actions (action_group,
391 modest_action_entries,
392 G_N_ELEMENTS (modest_action_entries),
394 gtk_action_group_add_toggle_actions (action_group,
395 msg_view_toggle_action_entries,
396 G_N_ELEMENTS (msg_view_toggle_action_entries),
399 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
400 g_object_unref (action_group);
402 /* Load the UI definition */
403 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
406 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
407 g_error_free (error);
412 /* Add accelerators */
413 gtk_window_add_accel_group (GTK_WINDOW (obj),
414 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
416 priv->is_search_result = FALSE;
417 priv->is_outbox = FALSE;
419 priv->msg_view = NULL;
420 priv->header_model = NULL;
421 priv->header_folder_id = NULL;
422 priv->clipboard_change_handler = 0;
423 priv->queue_change_handler = 0;
424 priv->account_removed_handler = 0;
425 priv->row_changed_handler = 0;
426 priv->row_deleted_handler = 0;
427 priv->row_inserted_handler = 0;
428 priv->rows_reordered_handler = 0;
429 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
431 priv->optimized_view = FALSE;
432 priv->progress_bar_timeout = 0;
433 priv->purge_timeout = 0;
434 priv->remove_attachment_banner = NULL;
435 priv->msg_uid = NULL;
437 priv->sighandlers = NULL;
440 init_window (MODEST_MSG_VIEW_WINDOW(obj));
442 /* Set window icon */
443 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
445 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
446 g_object_unref (window_icon);
449 hildon_program_add_window (hildon_program_get_instance(),
456 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
458 ModestMsgViewWindowPrivate *priv = NULL;
460 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
462 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
464 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
466 if (priv->progress_bar_timeout > 0) {
467 g_source_remove (priv->progress_bar_timeout);
468 priv->progress_bar_timeout = 0;
475 set_toolbar_mode (ModestMsgViewWindow *self,
476 ModestToolBarModes mode)
478 ModestWindowPrivate *parent_priv;
479 ModestMsgViewWindowPrivate *priv;
480 /* GtkWidget *widget = NULL; */
482 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
484 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
485 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
487 /* Sets current toolbar mode */
488 priv->current_toolbar_mode = mode;
490 /* Update toolbar dimming state */
491 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
494 case TOOLBAR_MODE_NORMAL:
495 if (priv->progress_toolitem) {
496 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
497 gtk_widget_hide (priv->progress_toolitem);
500 if (priv->progress_bar)
501 gtk_widget_hide (priv->progress_bar);
503 if (priv->cancel_toolitem)
504 gtk_widget_hide (priv->cancel_toolitem);
506 if (priv->prev_toolitem)
507 gtk_widget_show (priv->prev_toolitem);
509 if (priv->next_toolitem)
510 gtk_widget_show (priv->next_toolitem);
512 /* Hide toolbar if optimized view is enabled */
513 if (priv->optimized_view) {
514 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
515 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
519 case TOOLBAR_MODE_TRANSFER:
520 if (priv->prev_toolitem)
521 gtk_widget_hide (priv->prev_toolitem);
523 if (priv->next_toolitem)
524 gtk_widget_hide (priv->next_toolitem);
526 if (priv->progress_bar)
527 gtk_widget_show (priv->progress_bar);
529 if (priv->progress_toolitem) {
530 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
531 gtk_widget_show (priv->progress_toolitem);
534 if (priv->cancel_toolitem)
535 gtk_widget_show (priv->cancel_toolitem);
537 /* Show toolbar if it's hiden (optimized view ) */
538 if (priv->optimized_view) {
539 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
540 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
545 g_return_if_reached ();
552 init_window (ModestMsgViewWindow *obj)
554 GtkWidget *main_vbox;
555 ModestMsgViewWindowPrivate *priv;
557 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
559 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
560 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
561 main_vbox = gtk_vbox_new (FALSE, 6);
562 #ifdef MODEST_TOOLKIT_HILDON2
563 priv->main_scroll = hildon_pannable_area_new ();
564 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
566 #ifdef MODEST_USE_MOZEMBED
567 priv->main_scroll = priv->msg_view;
568 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
570 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
571 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
573 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
574 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
575 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
578 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
579 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
581 priv->find_toolbar = hildon_find_toolbar_new (NULL);
582 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
583 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
585 gtk_widget_show_all (GTK_WIDGET(main_vbox));
589 modest_msg_view_window_disconnect_signals (ModestWindow *self)
591 ModestMsgViewWindowPrivate *priv;
592 ModestHeaderView *header_view = NULL;
593 ModestWindow *main_window = NULL;
595 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
597 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
598 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
599 priv->clipboard_change_handler))
600 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
601 priv->clipboard_change_handler);
603 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
604 priv->queue_change_handler))
605 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
606 priv->queue_change_handler);
608 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
609 priv->account_removed_handler))
610 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
611 priv->account_removed_handler);
613 if (priv->header_model) {
614 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
615 priv->row_changed_handler))
616 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
617 priv->row_changed_handler);
619 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
620 priv->row_deleted_handler))
621 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
622 priv->row_deleted_handler);
624 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
625 priv->row_inserted_handler))
626 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
627 priv->row_inserted_handler);
629 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
630 priv->rows_reordered_handler))
631 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
632 priv->rows_reordered_handler);
635 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
636 priv->sighandlers = NULL;
638 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
639 FALSE); /* don't create */
643 header_view = MODEST_HEADER_VIEW(
644 modest_main_window_get_child_widget(
645 MODEST_MAIN_WINDOW(main_window),
646 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
647 if (header_view == NULL)
650 modest_header_view_remove_observer(header_view,
651 MODEST_HEADER_VIEW_OBSERVER(self));
655 modest_msg_view_window_finalize (GObject *obj)
657 ModestMsgViewWindowPrivate *priv;
659 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
661 /* Sanity check: shouldn't be needed, the window mgr should
662 call this function before */
663 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
665 if (priv->header_model != NULL) {
666 g_object_unref (priv->header_model);
667 priv->header_model = NULL;
670 if (priv->progress_bar_timeout > 0) {
671 g_source_remove (priv->progress_bar_timeout);
672 priv->progress_bar_timeout = 0;
675 if (priv->remove_attachment_banner) {
676 gtk_widget_destroy (priv->remove_attachment_banner);
677 g_object_unref (priv->remove_attachment_banner);
678 priv->remove_attachment_banner = NULL;
681 if (priv->purge_timeout > 0) {
682 g_source_remove (priv->purge_timeout);
683 priv->purge_timeout = 0;
686 if (priv->row_reference) {
687 gtk_tree_row_reference_free (priv->row_reference);
688 priv->row_reference = NULL;
691 if (priv->next_row_reference) {
692 gtk_tree_row_reference_free (priv->next_row_reference);
693 priv->next_row_reference = NULL;
697 g_free (priv->msg_uid);
698 priv->msg_uid = NULL;
701 G_OBJECT_CLASS(parent_class)->finalize (obj);
705 select_next_valid_row (GtkTreeModel *model,
706 GtkTreeRowReference **row_reference,
710 GtkTreeIter tmp_iter;
712 GtkTreePath *next = NULL;
713 gboolean retval = FALSE, finished;
715 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
717 path = gtk_tree_row_reference_get_path (*row_reference);
718 gtk_tree_model_get_iter (model, &tmp_iter, path);
719 gtk_tree_row_reference_free (*row_reference);
720 *row_reference = NULL;
724 TnyHeader *header = NULL;
726 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
727 gtk_tree_model_get (model, &tmp_iter,
728 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
732 if (msg_is_visible (header, is_outbox)) {
733 next = gtk_tree_model_get_path (model, &tmp_iter);
734 *row_reference = gtk_tree_row_reference_new (model, next);
735 gtk_tree_path_free (next);
739 g_object_unref (header);
742 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
743 next = gtk_tree_model_get_path (model, &tmp_iter);
745 /* Ensure that we are not selecting the same */
746 if (gtk_tree_path_compare (path, next) != 0) {
747 gtk_tree_model_get (model, &tmp_iter,
748 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
751 if (msg_is_visible (header, is_outbox)) {
752 *row_reference = gtk_tree_row_reference_new (model, next);
756 g_object_unref (header);
760 /* If we ended up in the same message
761 then there is no valid next
765 gtk_tree_path_free (next);
767 /* If there are no more messages and we don't
768 want to start again in the first one then
769 there is no valid next message */
775 gtk_tree_path_free (path);
780 /* TODO: This should be in _init(), with the parameters as properties. */
782 modest_msg_view_window_construct (ModestMsgViewWindow *self,
783 const gchar *modest_account_name,
784 const gchar *msg_uid)
787 ModestMsgViewWindowPrivate *priv = NULL;
788 ModestWindowPrivate *parent_priv = NULL;
789 ModestDimmingRulesGroup *menu_rules_group = NULL;
790 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
791 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
793 obj = G_OBJECT (self);
794 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
795 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
797 priv->msg_uid = g_strdup (msg_uid);
800 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
801 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
802 gtk_widget_show (parent_priv->menubar);
803 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
805 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
806 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
807 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
809 /* Add common dimming rules */
810 modest_dimming_rules_group_add_rules (menu_rules_group,
811 modest_msg_view_menu_dimming_entries,
812 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
813 MODEST_WINDOW (self));
814 modest_dimming_rules_group_add_rules (toolbar_rules_group,
815 modest_msg_view_toolbar_dimming_entries,
816 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
817 MODEST_WINDOW (self));
818 modest_dimming_rules_group_add_rules (clipboard_rules_group,
819 modest_msg_view_clipboard_dimming_entries,
820 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
821 MODEST_WINDOW (self));
823 /* Insert dimming rules group for this window */
824 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
825 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
826 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
827 g_object_unref (menu_rules_group);
828 g_object_unref (toolbar_rules_group);
829 g_object_unref (clipboard_rules_group);
831 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
833 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);
834 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
835 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
836 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
837 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
838 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
839 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
840 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
841 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
842 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
843 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
844 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
845 G_CALLBACK (on_fetch_image), obj);
847 g_signal_connect (G_OBJECT (obj), "key-release-event",
848 G_CALLBACK (modest_msg_view_window_key_event),
851 g_signal_connect (G_OBJECT (obj), "key-press-event",
852 G_CALLBACK (modest_msg_view_window_key_event),
855 g_signal_connect (G_OBJECT (obj), "move-focus",
856 G_CALLBACK (on_move_focus), obj);
858 /* Mail Operation Queue */
859 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
861 G_CALLBACK (on_queue_changed),
864 /* Account manager */
865 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
867 G_CALLBACK(on_account_removed),
870 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
872 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
873 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
874 priv->last_search = NULL;
876 /* Init the clipboard actions dim status */
877 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
879 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
884 /* FIXME: parameter checks */
886 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
887 const gchar *modest_account_name,
888 const gchar *msg_uid,
890 GtkTreeRowReference *row_reference)
892 ModestMsgViewWindow *window = NULL;
893 ModestMsgViewWindowPrivate *priv = NULL;
894 TnyFolder *header_folder = NULL;
895 ModestHeaderView *header_view = NULL;
896 ModestWindow *main_window = NULL;
897 ModestWindowMgr *mgr = NULL;
900 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
903 mgr = modest_runtime_get_window_mgr ();
904 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
905 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
907 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
909 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
911 /* Remember the message list's TreeModel so we can detect changes
912 * and change the list selection when necessary: */
914 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
916 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
917 MODEST_MAIN_WINDOW(main_window),
918 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
921 if (header_view != NULL){
922 header_folder = modest_header_view_get_folder(header_view);
923 /* This could happen if the header folder was
924 unseleted before opening this msg window (for
925 example if the user selects an account in the
926 folder view of the main window */
928 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
929 priv->header_folder_id = tny_folder_get_id(header_folder);
930 g_assert(priv->header_folder_id != NULL);
931 g_object_unref(header_folder);
935 /* Setup row references and connect signals */
936 priv->header_model = g_object_ref (model);
939 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
940 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
941 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
943 priv->row_reference = NULL;
944 priv->next_row_reference = NULL;
947 /* Connect signals */
948 priv->row_changed_handler =
949 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
950 G_CALLBACK(modest_msg_view_window_on_row_changed),
952 priv->row_deleted_handler =
953 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
954 G_CALLBACK(modest_msg_view_window_on_row_deleted),
956 priv->row_inserted_handler =
957 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
958 G_CALLBACK(modest_msg_view_window_on_row_inserted),
960 priv->rows_reordered_handler =
961 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
962 G_CALLBACK(modest_msg_view_window_on_row_reordered),
965 if (header_view != NULL){
966 modest_header_view_add_observer(header_view,
967 MODEST_HEADER_VIEW_OBSERVER(window));
970 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
971 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
972 /* gtk_widget_show_all (GTK_WIDGET (window)); */
973 modest_msg_view_window_update_priority (window);
975 /* Check dimming rules */
976 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
977 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
978 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
980 return MODEST_WINDOW(window);
984 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
985 const gchar *modest_account_name,
986 const gchar *msg_uid)
988 ModestMsgViewWindow *window = NULL;
989 ModestMsgViewWindowPrivate *priv = NULL;
990 ModestWindowMgr *mgr = NULL;
992 mgr = modest_runtime_get_window_mgr ();
993 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
994 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
995 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
997 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
999 /* Remember that this is a search result,
1000 * so we can disable some UI appropriately: */
1001 priv->is_search_result = TRUE;
1003 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1005 update_window_title (window);
1006 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1007 modest_msg_view_window_update_priority (window);
1009 /* Check dimming rules */
1010 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1011 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1012 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1014 return MODEST_WINDOW(window);
1018 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1019 const gchar *modest_account_name,
1020 const gchar *msg_uid)
1022 GObject *obj = NULL;
1023 ModestMsgViewWindowPrivate *priv;
1024 ModestWindowMgr *mgr = NULL;
1026 g_return_val_if_fail (msg, NULL);
1027 mgr = modest_runtime_get_window_mgr ();
1028 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1029 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1030 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1031 modest_account_name, msg_uid);
1033 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1034 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1036 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1038 /* Check dimming rules */
1039 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1040 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1041 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1043 return MODEST_WINDOW(obj);
1047 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1050 ModestMsgViewWindow *window)
1052 check_dimming_rules_after_change (window);
1056 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1058 ModestMsgViewWindow *window)
1060 check_dimming_rules_after_change (window);
1062 /* The window could have dissapeared */
1065 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1067 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1068 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1072 /* On insertions we check if the folder still has the message we are
1073 * showing or do not. If do not, we do nothing. Which means we are still
1074 * not attached to any header folder and thus next/prev buttons are
1075 * still dimmed. Once the message that is shown by msg-view is found, the
1076 * new model of header-view will be attached and the references will be set.
1077 * On each further insertions dimming rules will be checked. However
1078 * this requires extra CPU time at least works.
1079 * (An message might be deleted from TnyFolder and thus will not be
1080 * inserted into the model again for example if it is removed by the
1081 * imap server and the header view is refreshed.)
1084 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1085 GtkTreePath *tree_path,
1086 GtkTreeIter *tree_iter,
1087 ModestMsgViewWindow *window)
1089 ModestMsgViewWindowPrivate *priv = NULL;
1090 TnyHeader *header = NULL;
1092 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1093 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1095 g_assert (model == priv->header_model);
1097 /* Check if the newly inserted message is the same we are actually
1098 * showing. IF not, we should remain detached from the header model
1099 * and thus prev and next toolbar buttons should remain dimmed. */
1100 gtk_tree_model_get (model, tree_iter,
1101 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1104 if (TNY_IS_HEADER (header)) {
1107 uid = modest_tny_folder_get_header_unique_id (header);
1108 if (!g_str_equal(priv->msg_uid, uid)) {
1109 check_dimming_rules_after_change (window);
1111 g_object_unref (G_OBJECT(header));
1115 g_object_unref(G_OBJECT(header));
1118 if (priv->row_reference) {
1119 gtk_tree_row_reference_free (priv->row_reference);
1122 /* Setup row_reference for the actual msg. */
1123 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1124 if (priv->row_reference == NULL) {
1125 g_warning("No reference for msg header item.");
1129 /* Now set up next_row_reference. */
1130 if (priv->next_row_reference) {
1131 gtk_tree_row_reference_free (priv->next_row_reference);
1134 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1135 select_next_valid_row (priv->header_model,
1136 &(priv->next_row_reference), FALSE, priv->is_outbox);
1138 /* Connect the remaining callbacks to become able to detect
1139 * changes in header-view. */
1140 priv->row_changed_handler =
1141 g_signal_connect (priv->header_model, "row-changed",
1142 G_CALLBACK (modest_msg_view_window_on_row_changed),
1144 priv->row_deleted_handler =
1145 g_signal_connect (priv->header_model, "row-deleted",
1146 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1148 priv->rows_reordered_handler =
1149 g_signal_connect (priv->header_model, "rows-reordered",
1150 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1153 check_dimming_rules_after_change (window);
1157 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1161 ModestMsgViewWindow *window)
1163 ModestMsgViewWindowPrivate *priv = NULL;
1164 gboolean already_changed = FALSE;
1166 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1168 /* If the current row was reordered select the proper next
1169 valid row. The same if the next row reference changes */
1170 if (priv->row_reference &&
1171 gtk_tree_row_reference_valid (priv->row_reference)) {
1173 path = gtk_tree_row_reference_get_path (priv->row_reference);
1174 if (gtk_tree_path_compare (path, arg1) == 0) {
1175 if (priv->next_row_reference) {
1176 gtk_tree_row_reference_free (priv->next_row_reference);
1178 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1179 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1180 already_changed = TRUE;
1182 gtk_tree_path_free (path);
1184 if (!already_changed &&
1185 priv->next_row_reference &&
1186 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1188 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1189 if (gtk_tree_path_compare (path, arg1) == 0) {
1190 if (priv->next_row_reference) {
1191 gtk_tree_row_reference_free (priv->next_row_reference);
1193 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1194 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1196 gtk_tree_path_free (path);
1198 check_dimming_rules_after_change (window);
1201 /* The modest_msg_view_window_update_model_replaced implements update
1202 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1203 * actually belongs to the header-view is the same as the TnyFolder of
1204 * the message of msg-view or not. If they are different, there is
1205 * nothing to do. If they are the same, then the model has replaced and
1206 * the reference in msg-view shall be replaced from the old model to
1207 * the new model. In this case the view will be detached from it's
1208 * header folder. From this point the next/prev buttons are dimmed.
1211 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1212 GtkTreeModel *model,
1213 const gchar *tny_folder_id)
1215 ModestMsgViewWindowPrivate *priv = NULL;
1216 ModestMsgViewWindow *window = NULL;
1218 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1219 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1221 window = MODEST_MSG_VIEW_WINDOW(observer);
1222 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1224 /* If there is an other folder in the header-view then we do
1225 * not care about it's model (msg list). Else if the
1226 * header-view shows the folder the msg shown by us is in, we
1227 * shall replace our model reference and make some check. */
1228 if(model == NULL || tny_folder_id == NULL ||
1229 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1232 /* Model is changed(replaced), so we should forget the old
1233 * one. Because there might be other references and there
1234 * might be some change on the model even if we unreferenced
1235 * it, we need to disconnect our signals here. */
1236 if (priv->header_model) {
1237 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1238 priv->row_changed_handler))
1239 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1240 priv->row_changed_handler);
1241 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1242 priv->row_deleted_handler))
1243 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1244 priv->row_deleted_handler);
1245 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1246 priv->row_inserted_handler))
1247 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1248 priv->row_inserted_handler);
1249 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1250 priv->rows_reordered_handler))
1251 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1252 priv->rows_reordered_handler);
1255 if (priv->row_reference)
1256 gtk_tree_row_reference_free (priv->row_reference);
1257 if (priv->next_row_reference)
1258 gtk_tree_row_reference_free (priv->next_row_reference);
1259 g_object_unref(priv->header_model);
1262 priv->row_changed_handler = 0;
1263 priv->row_deleted_handler = 0;
1264 priv->row_inserted_handler = 0;
1265 priv->rows_reordered_handler = 0;
1266 priv->next_row_reference = NULL;
1267 priv->row_reference = NULL;
1268 priv->header_model = NULL;
1271 priv->header_model = g_object_ref (model);
1273 /* Also we must connect to the new model for row insertions.
1274 * Only for insertions now. We will need other ones only after
1275 * the msg is show by msg-view is added to the new model. */
1276 priv->row_inserted_handler =
1277 g_signal_connect (priv->header_model, "row-inserted",
1278 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1281 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1282 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1286 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1288 ModestMsgViewWindowPrivate *priv= NULL;
1290 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1291 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1293 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1297 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1299 ModestMsgViewWindowPrivate *priv= NULL;
1301 TnyHeader *header = NULL;
1302 GtkTreePath *path = NULL;
1305 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1306 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1308 /* If the message was not obtained from a treemodel,
1309 * for instance if it was opened directly by the search UI:
1311 if (priv->header_model == NULL ||
1312 priv->row_reference == NULL ||
1313 !gtk_tree_row_reference_valid (priv->row_reference)) {
1314 msg = modest_msg_view_window_get_message (self);
1316 header = tny_msg_get_header (msg);
1317 g_object_unref (msg);
1322 /* Get iter of the currently selected message in the header view: */
1323 path = gtk_tree_row_reference_get_path (priv->row_reference);
1324 g_return_val_if_fail (path != NULL, NULL);
1325 gtk_tree_model_get_iter (priv->header_model,
1329 /* Get current message header */
1330 gtk_tree_model_get (priv->header_model, &iter,
1331 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1334 gtk_tree_path_free (path);
1339 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1341 ModestMsgViewWindowPrivate *priv;
1343 g_return_val_if_fail (self, NULL);
1345 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1347 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1351 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1353 ModestMsgViewWindowPrivate *priv;
1355 g_return_val_if_fail (self, NULL);
1357 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1359 return (const gchar*) priv->msg_uid;
1363 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1366 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1367 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1368 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1372 is_active = gtk_toggle_action_get_active (toggle);
1375 gtk_widget_show (priv->find_toolbar);
1376 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1378 gtk_widget_hide (priv->find_toolbar);
1379 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1382 /* update the toggle buttons status */
1383 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1384 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1388 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1389 ModestMsgViewWindow *obj)
1391 GtkToggleAction *toggle;
1392 ModestWindowPrivate *parent_priv;
1393 ModestMsgViewWindowPrivate *priv;
1395 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1396 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1398 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1399 gtk_toggle_action_set_active (toggle, FALSE);
1400 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1404 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1405 ModestMsgViewWindow *obj)
1407 gchar *current_search;
1408 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1410 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1411 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1415 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1417 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1418 g_free (current_search);
1419 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1423 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1425 g_free (priv->last_search);
1426 priv->last_search = g_strdup (current_search);
1427 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1430 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1431 g_free (priv->last_search);
1432 priv->last_search = NULL;
1434 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1435 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1438 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1439 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1440 g_free (priv->last_search);
1441 priv->last_search = NULL;
1443 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1444 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1448 g_free (current_search);
1453 modest_msg_view_window_get_zoom (ModestWindow *window)
1455 ModestMsgViewWindowPrivate *priv;
1457 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1459 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1460 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1464 modest_msg_view_window_key_event (GtkWidget *window,
1470 focus = gtk_window_get_focus (GTK_WINDOW (window));
1472 /* for the find toolbar case */
1473 if (focus && GTK_IS_ENTRY (focus)) {
1474 if (event->keyval == GDK_BackSpace) {
1476 copy = gdk_event_copy ((GdkEvent *) event);
1477 gtk_widget_event (focus, copy);
1478 gdk_event_free (copy);
1483 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1484 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1485 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1486 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1487 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1488 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1489 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1490 /* gboolean return_value; */
1492 if (event->type == GDK_KEY_PRESS) {
1493 GtkScrollType scroll_type;
1495 switch (event->keyval) {
1498 scroll_type = GTK_SCROLL_STEP_UP; break;
1501 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1503 case GDK_KP_Page_Up:
1504 scroll_type = GTK_SCROLL_PAGE_UP; break;
1506 case GDK_KP_Page_Down:
1507 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1510 scroll_type = GTK_SCROLL_START; break;
1513 scroll_type = GTK_SCROLL_END; break;
1514 default: scroll_type = GTK_SCROLL_NONE;
1517 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1518 /* scroll_type, FALSE, &return_value); */
1529 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1532 ModestMsgViewWindowPrivate *priv;
1533 GtkTreeIter tmp_iter;
1534 gboolean is_last_selected;
1536 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1537 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1539 /*if no model (so no rows at all), then virtually we are the last*/
1540 if (!priv->header_model || !priv->row_reference)
1543 if (!gtk_tree_row_reference_valid (priv->row_reference))
1546 path = gtk_tree_row_reference_get_path (priv->row_reference);
1550 is_last_selected = TRUE;
1551 while (is_last_selected) {
1553 gtk_tree_path_next (path);
1554 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1556 gtk_tree_model_get (priv->header_model, &tmp_iter,
1557 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1560 if (msg_is_visible (header, priv->is_outbox))
1561 is_last_selected = FALSE;
1562 g_object_unref(G_OBJECT(header));
1565 gtk_tree_path_free (path);
1566 return is_last_selected;
1570 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1572 ModestMsgViewWindowPrivate *priv;
1574 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1575 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1577 return priv->header_model != NULL;
1581 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1583 ModestMsgViewWindowPrivate *priv;
1585 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1586 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1588 return priv->is_search_result;
1592 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1594 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1596 if (!check_outbox) {
1599 ModestTnySendQueueStatus status;
1600 status = modest_tny_all_send_queues_get_msg_status (header);
1601 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1602 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1607 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1610 ModestMsgViewWindowPrivate *priv;
1611 gboolean is_first_selected;
1612 GtkTreeIter tmp_iter;
1614 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1615 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1617 /*if no model (so no rows at all), then virtually we are the first*/
1618 if (!priv->header_model || !priv->row_reference)
1621 if (!gtk_tree_row_reference_valid (priv->row_reference))
1624 path = gtk_tree_row_reference_get_path (priv->row_reference);
1628 is_first_selected = TRUE;
1629 while (is_first_selected) {
1631 if(!gtk_tree_path_prev (path))
1633 /* Here the 'if' is needless for logic, but let make sure
1634 * iter is valid for gtk_tree_model_get. */
1635 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1637 gtk_tree_model_get (priv->header_model, &tmp_iter,
1638 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1641 if (msg_is_visible (header, priv->is_outbox))
1642 is_first_selected = FALSE;
1643 g_object_unref(G_OBJECT(header));
1646 gtk_tree_path_free (path);
1647 return is_first_selected;
1652 GtkTreeRowReference *row_reference;
1656 message_reader_performer (gboolean canceled,
1658 GtkWindow *parent_window,
1659 TnyAccount *account,
1662 ModestMailOperation *mail_op = NULL;
1663 MsgReaderInfo *info;
1665 info = (MsgReaderInfo *) user_data;
1666 if (canceled || err) {
1670 /* Register the header - it'll be unregistered in the callback */
1671 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1673 /* New mail operation */
1674 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1675 modest_ui_actions_disk_operations_error_handler,
1678 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1679 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1680 g_object_unref (mail_op);
1682 /* Update dimming rules */
1683 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1684 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1687 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1688 g_object_unref (info->header);
1689 g_slice_free (MsgReaderInfo, info);
1694 * Reads the message whose summary item is @header. It takes care of
1695 * several things, among others:
1697 * If the message was not previously downloaded then ask the user
1698 * before downloading. If there is no connection launch the connection
1699 * dialog. Update toolbar dimming rules.
1701 * Returns: TRUE if the mail operation was started, otherwise if the
1702 * user do not want to download the message, or if the user do not
1703 * want to connect, then the operation is not issued
1706 message_reader (ModestMsgViewWindow *window,
1707 ModestMsgViewWindowPrivate *priv,
1709 GtkTreeRowReference *row_reference)
1711 ModestWindowMgr *mgr;
1712 TnyAccount *account;
1714 MsgReaderInfo *info;
1716 g_return_val_if_fail (row_reference != NULL, FALSE);
1718 mgr = modest_runtime_get_window_mgr ();
1719 /* Msg download completed */
1720 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1721 /* Ask the user if he wants to download the message if
1723 if (!tny_device_is_online (modest_runtime_get_device())) {
1724 GtkResponseType response;
1726 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1727 _("mcen_nc_get_msg"));
1728 if (response == GTK_RESPONSE_CANCEL)
1731 folder = tny_header_get_folder (header);
1732 info = g_slice_new (MsgReaderInfo);
1733 info->header = g_object_ref (header);
1734 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1736 /* Offer the connection dialog if necessary */
1737 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1739 TNY_FOLDER_STORE (folder),
1740 message_reader_performer,
1742 g_object_unref (folder);
1747 folder = tny_header_get_folder (header);
1748 account = tny_folder_get_account (folder);
1749 info = g_slice_new (MsgReaderInfo);
1750 info->header = g_object_ref (header);
1751 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1753 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1754 g_object_unref (account);
1755 g_object_unref (folder);
1761 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1763 ModestMsgViewWindowPrivate *priv;
1764 GtkTreePath *path= NULL;
1765 GtkTreeIter tmp_iter;
1767 gboolean retval = TRUE;
1768 GtkTreeRowReference *row_reference = NULL;
1770 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1771 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1773 if (!priv->row_reference)
1776 /* Update the next row reference if it's not valid. This could
1777 happen if for example the header which it was pointing to,
1778 was deleted. The best place to do it is in the row-deleted
1779 handler but the tinymail model do not work like the glib
1780 tree models and reports the deletion when the row is still
1782 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1783 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1784 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1785 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1788 if (priv->next_row_reference)
1789 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1793 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1795 gtk_tree_model_get_iter (priv->header_model,
1798 gtk_tree_path_free (path);
1800 gtk_tree_model_get (priv->header_model, &tmp_iter,
1801 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1804 /* Read the message & show it */
1805 if (!message_reader (window, priv, header, row_reference)) {
1808 gtk_tree_row_reference_free (row_reference);
1811 g_object_unref (header);
1817 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1819 ModestMsgViewWindowPrivate *priv = NULL;
1821 gboolean finished = FALSE;
1822 gboolean retval = FALSE;
1824 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1825 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1827 /* Return inmediatly if there is no header model */
1828 if (!priv->header_model || !priv->row_reference)
1831 path = gtk_tree_row_reference_get_path (priv->row_reference);
1832 while (!finished && gtk_tree_path_prev (path)) {
1836 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1837 gtk_tree_model_get (priv->header_model, &iter,
1838 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1842 if (msg_is_visible (header, priv->is_outbox)) {
1843 GtkTreeRowReference *row_reference;
1844 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1845 /* Read the message & show it */
1846 retval = message_reader (window, priv, header, row_reference);
1847 gtk_tree_row_reference_free (row_reference);
1851 g_object_unref (header);
1855 gtk_tree_path_free (path);
1860 view_msg_cb (ModestMailOperation *mail_op,
1867 ModestMsgViewWindow *self = NULL;
1868 ModestMsgViewWindowPrivate *priv = NULL;
1869 GtkTreeRowReference *row_reference = NULL;
1871 /* Unregister the header (it was registered before creating the mail operation) */
1872 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1874 row_reference = (GtkTreeRowReference *) user_data;
1876 gtk_tree_row_reference_free (row_reference);
1880 /* If there was any error */
1881 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1882 gtk_tree_row_reference_free (row_reference);
1886 /* Get the window */
1887 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
1888 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
1889 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1891 /* Update the row reference */
1892 if (priv->row_reference != NULL) {
1893 gtk_tree_row_reference_free (priv->row_reference);
1894 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
1895 if (priv->next_row_reference != NULL) {
1896 gtk_tree_row_reference_free (priv->next_row_reference);
1898 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1899 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
1902 /* Mark header as read */
1903 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
1904 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
1906 /* Set new message */
1907 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
1908 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1909 modest_msg_view_window_update_priority (self);
1910 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
1911 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1914 /* Set the new message uid of the window */
1915 if (priv->msg_uid) {
1916 g_free (priv->msg_uid);
1917 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
1920 /* Notify the observers */
1921 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
1922 0, priv->header_model, priv->row_reference);
1925 g_object_unref (self);
1926 gtk_tree_row_reference_free (row_reference);
1930 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
1932 ModestMsgViewWindowPrivate *priv;
1934 TnyFolderType folder_type;
1936 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1938 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
1940 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1944 folder = tny_msg_get_folder (msg);
1946 folder_type = modest_tny_folder_guess_folder_type (folder);
1947 g_object_unref (folder);
1949 g_object_unref (msg);
1957 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
1959 ModestMsgViewWindowPrivate *priv;
1960 TnyHeader *header = NULL;
1961 TnyHeaderFlags flags = 0;
1963 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1965 if (priv->header_model && priv->row_reference) {
1967 GtkTreePath *path = NULL;
1969 path = gtk_tree_row_reference_get_path (priv->row_reference);
1970 g_return_if_fail (path != NULL);
1971 gtk_tree_model_get_iter (priv->header_model,
1973 gtk_tree_row_reference_get_path (priv->row_reference));
1975 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1977 gtk_tree_path_free (path);
1980 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1982 header = tny_msg_get_header (msg);
1983 g_object_unref (msg);
1988 flags = tny_header_get_flags (header);
1989 g_object_unref(G_OBJECT(header));
1992 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
1997 toolbar_resize (ModestMsgViewWindow *self)
1999 ModestMsgViewWindowPrivate *priv = NULL;
2000 ModestWindowPrivate *parent_priv = NULL;
2002 gint static_button_size;
2003 ModestWindowMgr *mgr;
2005 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2006 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2007 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2009 mgr = modest_runtime_get_window_mgr ();
2010 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2012 if (parent_priv->toolbar) {
2013 /* left size buttons */
2014 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2015 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2016 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2017 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2018 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2019 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2020 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2021 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2022 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2023 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2024 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2025 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2026 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2027 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2028 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2029 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2031 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2032 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2033 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2034 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2035 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2036 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2037 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2038 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2044 modest_msg_view_window_show_toolbar (ModestWindow *self,
2045 gboolean show_toolbar)
2047 ModestMsgViewWindowPrivate *priv = NULL;
2048 ModestWindowPrivate *parent_priv;
2049 GtkWidget *reply_button = NULL, *menu = NULL;
2050 GtkWidget *placeholder = NULL;
2053 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2054 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2056 /* Set optimized view status */
2057 priv->optimized_view = !show_toolbar;
2059 if (!parent_priv->toolbar) {
2060 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2062 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2064 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2065 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2066 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2067 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2068 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2070 /* Add ProgressBar (Transfer toolbar) */
2071 priv->progress_bar = modest_progress_bar_new ();
2072 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2073 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2074 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2075 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2076 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2078 /* Connect cancel 'clicked' signal to abort progress mode */
2079 g_signal_connect(priv->cancel_toolitem, "clicked",
2080 G_CALLBACK(cancel_progressbar),
2083 /* Add it to the observers list */
2084 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2087 hildon_window_add_toolbar (HILDON_WINDOW (self),
2088 GTK_TOOLBAR (parent_priv->toolbar));
2090 /* Set reply button tap and hold menu */
2091 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2092 "/ToolBar/ToolbarMessageReply");
2093 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2094 "/ToolbarReplyCSM");
2095 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2099 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2100 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2101 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2103 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2104 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2105 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2107 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2110 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2111 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2116 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2118 ModestMsgViewWindow *window)
2120 if (!GTK_WIDGET_VISIBLE (window))
2123 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2127 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2129 ModestMsgViewWindowPrivate *priv;
2131 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2132 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2134 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2138 cancel_progressbar (GtkToolButton *toolbutton,
2139 ModestMsgViewWindow *self)
2142 ModestMsgViewWindowPrivate *priv;
2144 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2146 /* Get operation observers and cancel its current operation */
2147 tmp = priv->progress_widgets;
2149 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2150 tmp=g_slist_next(tmp);
2154 observers_empty (ModestMsgViewWindow *self)
2157 ModestMsgViewWindowPrivate *priv;
2158 gboolean is_empty = TRUE;
2159 guint pending_ops = 0;
2161 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2162 tmp = priv->progress_widgets;
2164 /* Check all observers */
2165 while (tmp && is_empty) {
2166 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2167 is_empty = pending_ops == 0;
2169 tmp = g_slist_next(tmp);
2176 on_account_removed (TnyAccountStore *account_store,
2177 TnyAccount *account,
2180 /* Do nothing if it's a transport account, because we only
2181 show the messages of a store account */
2182 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2183 const gchar *parent_acc = NULL;
2184 const gchar *our_acc = NULL;
2186 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2187 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2189 /* Close this window if I'm showing a message of the removed account */
2190 if (strcmp (parent_acc, our_acc) == 0)
2191 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2196 on_mail_operation_started (ModestMailOperation *mail_op,
2199 ModestMsgViewWindow *self;
2200 ModestMailOperationTypeOperation op_type;
2202 ModestMsgViewWindowPrivate *priv;
2203 GObject *source = NULL;
2205 self = MODEST_MSG_VIEW_WINDOW (user_data);
2206 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2207 op_type = modest_mail_operation_get_type_operation (mail_op);
2208 tmp = priv->progress_widgets;
2209 source = modest_mail_operation_get_source(mail_op);
2210 if (G_OBJECT (self) == source) {
2211 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2212 set_toolbar_transfer_mode(self);
2214 modest_progress_object_add_operation (
2215 MODEST_PROGRESS_OBJECT (tmp->data),
2217 tmp = g_slist_next (tmp);
2221 g_object_unref (source);
2225 on_mail_operation_finished (ModestMailOperation *mail_op,
2228 ModestMsgViewWindow *self;
2229 ModestMailOperationTypeOperation op_type;
2231 ModestMsgViewWindowPrivate *priv;
2233 self = MODEST_MSG_VIEW_WINDOW (user_data);
2234 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2235 op_type = modest_mail_operation_get_type_operation (mail_op);
2236 tmp = priv->progress_widgets;
2238 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2240 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2242 tmp = g_slist_next (tmp);
2245 /* If no more operations are being observed, NORMAL mode is enabled again */
2246 if (observers_empty (self)) {
2247 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2250 /* Update dimming rules. We have to do this right here
2251 and not in view_msg_cb because at that point the
2252 transfer mode is still enabled so the dimming rule
2253 won't let the user delete the message that has been
2254 readed for example */
2255 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2256 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2261 on_queue_changed (ModestMailOperationQueue *queue,
2262 ModestMailOperation *mail_op,
2263 ModestMailOperationQueueNotification type,
2264 ModestMsgViewWindow *self)
2266 ModestMsgViewWindowPrivate *priv;
2268 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2270 /* If this operations was created by another window, do nothing */
2271 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2274 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2275 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2277 "operation-started",
2278 G_CALLBACK (on_mail_operation_started),
2280 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2282 "operation-finished",
2283 G_CALLBACK (on_mail_operation_finished),
2285 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2286 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2288 "operation-started");
2289 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2291 "operation-finished");
2296 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2298 ModestMsgViewWindowPrivate *priv;
2299 TnyList *selected_attachments = NULL;
2301 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2302 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2304 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2306 return selected_attachments;
2312 guint banner_idle_id;
2313 } DecodeAsyncHelper;
2316 decode_async_banner_idle (gpointer user_data)
2318 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2320 helper->banner_idle_id = 0;
2321 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2322 g_object_ref (helper->banner);
2328 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2334 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2336 if (helper->banner_idle_id > 0) {
2337 g_source_remove (helper->banner_idle_id);
2338 helper->banner_idle_id = 0;
2340 if (helper->banner) {
2341 gtk_widget_destroy (helper->banner);
2343 if (cancelled || err) {
2344 modest_platform_information_banner (NULL, NULL,
2345 _("mail_ib_file_operation_failed"));
2349 /* make the file read-only */
2350 g_chmod(helper->filepath, 0444);
2352 /* Activate the file */
2353 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2357 g_free (helper->filepath);
2358 g_object_unref (helper->banner);
2359 g_slice_free (DecodeAsyncHelper, helper);
2363 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2364 TnyMimePart *mime_part)
2366 ModestMsgViewWindowPrivate *priv;
2367 const gchar *msg_uid;
2368 gchar *attachment_uid = NULL;
2369 gint attachment_index = 0;
2370 TnyList *attachments;
2372 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2373 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2374 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2376 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2377 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2378 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2379 g_object_unref (attachments);
2381 if (msg_uid && attachment_index >= 0) {
2382 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2385 if (mime_part == NULL) {
2386 gboolean error = FALSE;
2387 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2388 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2390 } else if (tny_list_get_length (selected_attachments) > 1) {
2391 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2395 iter = tny_list_create_iterator (selected_attachments);
2396 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2397 g_object_unref (iter);
2399 g_object_unref (selected_attachments);
2404 g_object_ref (mime_part);
2407 if (tny_mime_part_is_purged (mime_part)) {
2408 g_object_unref (mime_part);
2412 if (!modest_tny_mime_part_is_msg (mime_part)) {
2413 gchar *filepath = NULL;
2414 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2415 gboolean show_error_banner = FALSE;
2416 TnyFsStream *temp_stream = NULL;
2417 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2420 if (temp_stream != NULL) {
2421 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2422 helper->filepath = g_strdup (filepath);
2423 helper->banner = NULL;
2424 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2425 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2426 on_decode_to_stream_async_handler,
2429 g_object_unref (temp_stream);
2430 /* NOTE: files in the temporary area will be automatically
2431 * cleaned after some time if they are no longer in use */
2434 const gchar *content_type;
2435 /* the file may already exist but it isn't writable,
2436 * let's try to open it anyway */
2437 content_type = tny_mime_part_get_content_type (mime_part);
2438 modest_platform_activate_file (filepath, content_type);
2440 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2441 show_error_banner = TRUE;
2446 if (show_error_banner)
2447 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2449 /* message attachment */
2450 TnyHeader *header = NULL;
2451 ModestWindowMgr *mgr;
2452 ModestWindow *msg_win = NULL;
2455 header = tny_msg_get_header (TNY_MSG (mime_part));
2456 mgr = modest_runtime_get_window_mgr ();
2457 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2460 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2461 * thus, we don't do anything */
2462 g_warning ("window for is already being created");
2464 /* it's not found, so create a new window for it */
2465 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2466 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2468 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2469 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2470 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2471 modest_window_get_zoom (MODEST_WINDOW (window)));
2472 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2473 gtk_widget_show_all (GTK_WIDGET (msg_win));
2476 g_object_unref (mime_part);
2489 GnomeVFSResult result;
2492 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2493 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2494 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2495 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2498 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2502 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2503 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2504 g_free (pair->filename);
2505 g_object_unref (pair->part);
2506 g_slice_free (SaveMimePartPair, pair);
2508 g_list_free (info->pairs);
2511 gtk_widget_destroy (info->banner);
2512 g_slice_free (SaveMimePartInfo, info);
2517 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2519 if (info->pairs != NULL) {
2520 save_mime_part_to_file (info);
2522 /* This is a GDK lock because we are an idle callback and
2523 * hildon_banner_show_information is or does Gtk+ code */
2525 gdk_threads_enter (); /* CHECKED */
2526 save_mime_part_info_free (info, TRUE);
2527 if (info->result == GNOME_VFS_OK) {
2528 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2529 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2530 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2531 "cerm_device_memory_full"));
2533 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2535 gdk_threads_leave (); /* CHECKED */
2542 save_mime_part_to_file (SaveMimePartInfo *info)
2544 GnomeVFSHandle *handle;
2546 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2548 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2549 if (info->result == GNOME_VFS_OK) {
2550 GError *error = NULL;
2551 stream = tny_vfs_stream_new (handle);
2552 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2553 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2555 info->result = GNOME_VFS_ERROR_IO;
2557 g_object_unref (G_OBJECT (stream));
2558 g_object_unref (pair->part);
2559 g_slice_free (SaveMimePartPair, pair);
2560 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2562 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2563 save_mime_part_info_free (info, FALSE);
2566 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2571 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2573 gboolean is_ok = TRUE;
2574 gint replaced_files = 0;
2575 const GList *files = info->pairs;
2578 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2579 SaveMimePartPair *pair = iter->data;
2580 if (modest_utils_file_exists (pair->filename)) {
2584 if (replaced_files) {
2585 GtkWidget *confirm_overwrite_dialog;
2586 const gchar *message = (replaced_files == 1) ?
2587 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2588 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2589 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2592 gtk_widget_destroy (confirm_overwrite_dialog);
2596 save_mime_part_info_free (info, TRUE);
2598 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2599 _CS("sfil_ib_saving"));
2600 info->banner = banner;
2601 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2607 save_attachments_response (GtkDialog *dialog,
2611 TnyList *mime_parts;
2613 GList *files_to_save = NULL;
2615 mime_parts = TNY_LIST (user_data);
2617 if (arg1 != GTK_RESPONSE_OK)
2620 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2622 if (!modest_utils_folder_writable (chooser_uri)) {
2623 hildon_banner_show_information
2624 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2628 iter = tny_list_create_iterator (mime_parts);
2629 while (!tny_iterator_is_done (iter)) {
2630 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2632 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2633 !tny_mime_part_is_purged (mime_part) &&
2634 (tny_mime_part_get_filename (mime_part) != NULL)) {
2635 SaveMimePartPair *pair;
2637 pair = g_slice_new0 (SaveMimePartPair);
2639 if (tny_list_get_length (mime_parts) > 1) {
2641 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2642 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2645 pair->filename = g_strdup (chooser_uri);
2647 pair->part = mime_part;
2648 files_to_save = g_list_prepend (files_to_save, pair);
2650 tny_iterator_next (iter);
2652 g_object_unref (iter);
2654 g_free (chooser_uri);
2656 if (files_to_save != NULL) {
2657 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2658 info->pairs = files_to_save;
2659 info->result = TRUE;
2660 save_mime_parts_to_file_with_checks (info);
2664 /* Free and close the dialog */
2665 g_object_unref (mime_parts);
2666 gtk_widget_destroy (GTK_WIDGET (dialog));
2670 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2672 ModestMsgViewWindowPrivate *priv;
2673 GtkWidget *save_dialog = NULL;
2674 gchar *folder = NULL;
2675 gchar *filename = NULL;
2676 gchar *save_multiple_str = NULL;
2678 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2679 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2681 if (mime_parts == NULL) {
2682 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2683 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2686 g_object_ref (mime_parts);
2689 /* prepare dialog */
2690 if (tny_list_get_length (mime_parts) == 1) {
2692 /* only one attachment selected */
2693 iter = tny_list_create_iterator (mime_parts);
2694 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2695 g_object_unref (iter);
2696 if (!modest_tny_mime_part_is_msg (mime_part) &&
2697 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2698 !tny_mime_part_is_purged (mime_part)) {
2699 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2701 /* TODO: show any error? */
2702 g_warning ("Tried to save a non-file attachment");
2703 g_object_unref (mime_parts);
2706 g_object_unref (mime_part);
2708 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2709 tny_list_get_length (mime_parts));
2712 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2713 GTK_FILE_CHOOSER_ACTION_SAVE);
2716 folder = g_build_filename (g_get_home_dir (), g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2717 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2722 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2727 /* if multiple, set multiple string */
2728 if (save_multiple_str) {
2729 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2730 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2733 /* We must run this asynchronously, because the hildon dialog
2734 performs a gtk_dialog_run by itself which leads to gdk
2736 g_signal_connect (save_dialog, "response",
2737 G_CALLBACK (save_attachments_response), mime_parts);
2739 gtk_widget_show_all (save_dialog);
2743 show_remove_attachment_information (gpointer userdata)
2745 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2746 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2748 /* We're outside the main lock */
2749 gdk_threads_enter ();
2751 if (priv->remove_attachment_banner != NULL) {
2752 gtk_widget_destroy (priv->remove_attachment_banner);
2753 g_object_unref (priv->remove_attachment_banner);
2756 priv->remove_attachment_banner = g_object_ref (
2757 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2759 gdk_threads_leave ();
2765 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2767 ModestMsgViewWindowPrivate *priv;
2768 TnyList *mime_parts = NULL;
2769 gchar *confirmation_message;
2775 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2776 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2779 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2781 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2783 /* Remove already purged messages from mime parts list */
2784 iter = tny_list_create_iterator (mime_parts);
2785 while (!tny_iterator_is_done (iter)) {
2786 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2787 tny_iterator_next (iter);
2788 if (tny_mime_part_is_purged (part)) {
2789 tny_list_remove (mime_parts, (GObject *) part);
2791 g_object_unref (part);
2793 g_object_unref (iter);
2795 if (tny_list_get_length (mime_parts) == 0) {
2796 g_object_unref (mime_parts);
2800 n_attachments = tny_list_get_length (mime_parts);
2801 if (n_attachments == 1) {
2805 iter = tny_list_create_iterator (mime_parts);
2806 part = (TnyMimePart *) tny_iterator_get_current (iter);
2807 g_object_unref (iter);
2808 if (modest_tny_mime_part_is_msg (part)) {
2810 header = tny_msg_get_header (TNY_MSG (part));
2811 filename = tny_header_dup_subject (header);
2812 g_object_unref (header);
2813 if (filename == NULL)
2814 filename = g_strdup (_("mail_va_no_subject"));
2816 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2818 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2820 g_object_unref (part);
2822 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2823 "mcen_nc_purge_files_text",
2824 n_attachments), n_attachments);
2826 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2827 confirmation_message);
2828 g_free (confirmation_message);
2830 if (response != GTK_RESPONSE_OK) {
2831 g_object_unref (mime_parts);
2835 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2837 iter = tny_list_create_iterator (mime_parts);
2838 while (!tny_iterator_is_done (iter)) {
2841 part = (TnyMimePart *) tny_iterator_get_current (iter);
2842 tny_mime_part_set_purged (TNY_MIME_PART (part));
2843 g_object_unref (part);
2844 tny_iterator_next (iter);
2846 g_object_unref (iter);
2848 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2849 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2850 tny_msg_rewrite_cache (msg);
2851 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2852 g_object_unref (msg);
2854 g_object_unref (mime_parts);
2856 if (priv->purge_timeout > 0) {
2857 g_source_remove (priv->purge_timeout);
2858 priv->purge_timeout = 0;
2861 if (priv->remove_attachment_banner) {
2862 gtk_widget_destroy (priv->remove_attachment_banner);
2863 g_object_unref (priv->remove_attachment_banner);
2864 priv->remove_attachment_banner = NULL;
2872 update_window_title (ModestMsgViewWindow *window)
2874 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2876 TnyHeader *header = NULL;
2877 gchar *subject = NULL;
2879 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2882 header = tny_msg_get_header (msg);
2883 subject = tny_header_dup_subject (header);
2884 g_object_unref (header);
2885 g_object_unref (msg);
2888 if ((subject == NULL)||(subject[0] == '\0')) {
2890 subject = g_strdup (_("mail_va_no_subject"));
2893 gtk_window_set_title (GTK_WINDOW (window), subject);
2897 static void on_move_focus (GtkWidget *widget,
2898 GtkDirectionType direction,
2901 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
2905 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
2907 GnomeVFSResult result;
2908 GnomeVFSHandle *handle = NULL;
2909 GnomeVFSFileInfo *info = NULL;
2912 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2913 if (result != GNOME_VFS_OK) {
2918 info = gnome_vfs_file_info_new ();
2919 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
2920 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
2921 /* We put a "safe" default size for going to cache */
2922 *expected_size = (300*1024);
2924 *expected_size = info->size;
2926 gnome_vfs_file_info_unref (info);
2928 stream = tny_vfs_stream_new (handle);
2937 TnyStream *output_stream;
2938 GtkWidget *msg_view;
2942 on_fetch_image_idle_refresh_view (gpointer userdata)
2945 FetchImageData *fidata = (FetchImageData *) userdata;
2946 g_message ("REFRESH VIEW");
2947 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
2948 g_message ("QUEUING DRAW");
2949 gtk_widget_queue_draw (fidata->msg_view);
2951 g_object_unref (fidata->msg_view);
2952 g_slice_free (FetchImageData, fidata);
2957 on_fetch_image_thread (gpointer userdata)
2959 FetchImageData *fidata = (FetchImageData *) userdata;
2960 TnyStreamCache *cache;
2961 TnyStream *cache_stream;
2963 cache = modest_runtime_get_images_cache ();
2964 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
2965 g_free (fidata->cache_id);
2966 g_free (fidata->uri);
2968 if (cache_stream != NULL) {
2969 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
2970 tny_stream_close (cache_stream);
2971 g_object_unref (cache_stream);
2974 tny_stream_close (fidata->output_stream);
2975 g_object_unref (fidata->output_stream);
2978 gdk_threads_enter ();
2979 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
2980 gdk_threads_leave ();
2986 on_fetch_image (ModestMsgView *msgview,
2989 ModestMsgViewWindow *window)
2991 const gchar *current_account;
2992 ModestMsgViewWindowPrivate *priv;
2993 FetchImageData *fidata;
2995 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2997 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
2999 fidata = g_slice_new0 (FetchImageData);
3000 fidata->msg_view = g_object_ref (msgview);
3001 fidata->uri = g_strdup (uri);
3002 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3003 fidata->output_stream = g_object_ref (stream);
3005 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3006 g_object_unref (fidata->output_stream);
3007 g_free (fidata->cache_id);
3008 g_free (fidata->uri);
3009 g_object_unref (fidata->msg_view);
3010 g_slice_free (FetchImageData, fidata);
3011 tny_stream_close (stream);
3019 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3021 ModestMsgViewWindowPrivate *priv;
3022 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3023 GSList *recipients = NULL;
3025 gboolean contacts_to_add = FALSE;
3027 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3028 if (msg == NULL) return;
3029 recipients = modest_tny_msg_get_all_recipients_list (msg);
3031 if (recipients != NULL) {
3032 GtkWidget *picker_dialog;
3033 GtkWidget *selector;
3037 selector = hildon_touch_selector_new_text ();
3038 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3039 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3040 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3041 (const gchar *) node->data);
3042 contacts_to_add = TRUE;
3046 if (contacts_to_add) {
3048 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3049 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3051 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3052 HILDON_TOUCH_SELECTOR (selector));
3054 gtk_dialog_run (GTK_DIALOG (picker_dialog));
3055 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3056 gtk_widget_destroy (picker_dialog);
3059 modest_address_book_add_address (selected);
3064 g_object_unref (selector);
3069 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3070 g_object_unref (msg);