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 <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
70 #define MYDOCS_ENV "MYDOCSDIR"
71 #define DOCS_FOLDER ".documents"
73 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
74 struct _ModestMsgViewWindowPrivate {
77 GtkWidget *main_scroll;
78 GtkWidget *find_toolbar;
81 /* Progress observers */
82 GSList *progress_widgets;
85 GtkWidget *prev_toolitem;
86 GtkWidget *next_toolitem;
87 gboolean progress_hint;
89 /* Optimized view enabled */
90 gboolean optimized_view;
92 /* Whether this was created via the *_new_for_search_result() function. */
93 gboolean is_search_result;
95 /* Whether the message is in outbox */
98 /* A reference to the @model of the header view
99 * to allow selecting previous/next messages,
100 * if the message is currently selected in the header view.
102 const gchar *header_folder_id;
103 GtkTreeModel *header_model;
104 GtkTreeRowReference *row_reference;
105 GtkTreeRowReference *next_row_reference;
107 gulong clipboard_change_handler;
108 gulong queue_change_handler;
109 gulong account_removed_handler;
110 gulong row_changed_handler;
111 gulong row_deleted_handler;
112 gulong row_inserted_handler;
113 gulong rows_reordered_handler;
116 GtkWidget *remove_attachment_banner;
123 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
124 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
125 static void modest_header_view_observer_init(
126 ModestHeaderViewObserverIface *iface_class);
127 static void modest_msg_view_window_finalize (GObject *obj);
128 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
130 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
131 ModestMsgViewWindow *obj);
132 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
133 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
137 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
138 static void modest_msg_view_window_set_zoom (ModestWindow *window,
140 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
141 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
142 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
145 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
147 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
148 gboolean show_toolbar);
150 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
152 ModestMsgViewWindow *window);
154 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
157 ModestMsgViewWindow *window);
159 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
161 ModestMsgViewWindow *window);
163 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
164 GtkTreePath *tree_path,
165 GtkTreeIter *tree_iter,
166 ModestMsgViewWindow *window);
168 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
172 ModestMsgViewWindow *window);
174 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
176 const gchar *tny_folder_id);
178 static void on_queue_changed (ModestMailOperationQueue *queue,
179 ModestMailOperation *mail_op,
180 ModestMailOperationQueueNotification type,
181 ModestMsgViewWindow *self);
183 static void on_account_removed (TnyAccountStore *account_store,
187 static void on_move_focus (GtkWidget *widget,
188 GtkDirectionType direction,
191 static void view_msg_cb (ModestMailOperation *mail_op,
198 static void set_progress_hint (ModestMsgViewWindow *self,
201 static void update_window_title (ModestMsgViewWindow *window);
203 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
204 static void init_window (ModestMsgViewWindow *obj);
206 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
208 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
210 static gboolean on_fetch_image (ModestMsgView *msgview,
213 ModestMsgViewWindow *window);
215 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
216 GtkScrollType scroll_type,
219 static gboolean message_reader (ModestMsgViewWindow *window,
220 ModestMsgViewWindowPrivate *priv,
222 GtkTreeRowReference *row_reference);
224 static void setup_menu (ModestMsgViewWindow *self);
226 /* list my signals */
233 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
234 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
238 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
239 MODEST_TYPE_MSG_VIEW_WINDOW, \
240 ModestMsgViewWindowPrivate))
242 static GtkWindowClass *parent_class = NULL;
244 /* uncomment the following if you have defined any signals */
245 static guint signals[LAST_SIGNAL] = {0};
248 modest_msg_view_window_get_type (void)
250 static GType my_type = 0;
252 static const GTypeInfo my_info = {
253 sizeof(ModestMsgViewWindowClass),
254 NULL, /* base init */
255 NULL, /* base finalize */
256 (GClassInitFunc) modest_msg_view_window_class_init,
257 NULL, /* class finalize */
258 NULL, /* class data */
259 sizeof(ModestMsgViewWindow),
261 (GInstanceInitFunc) modest_msg_view_window_init,
264 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
265 "ModestMsgViewWindow",
268 static const GInterfaceInfo modest_header_view_observer_info =
270 (GInterfaceInitFunc) modest_header_view_observer_init,
271 NULL, /* interface_finalize */
272 NULL /* interface_data */
275 g_type_add_interface_static (my_type,
276 MODEST_TYPE_HEADER_VIEW_OBSERVER,
277 &modest_header_view_observer_info);
283 save_state (ModestWindow *self)
285 modest_widget_memory_save (modest_runtime_get_conf (),
287 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
291 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
292 GtkScrollType scroll_type,
296 ModestMsgViewWindowPrivate *priv;
297 gboolean return_value;
299 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
300 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
305 add_scroll_binding (GtkBindingSet *binding_set,
307 GtkScrollType scroll)
309 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
311 gtk_binding_entry_add_signal (binding_set, keyval, 0,
313 GTK_TYPE_SCROLL_TYPE, scroll,
314 G_TYPE_BOOLEAN, FALSE);
315 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
317 GTK_TYPE_SCROLL_TYPE, scroll,
318 G_TYPE_BOOLEAN, FALSE);
322 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
324 GObjectClass *gobject_class;
325 HildonWindowClass *hildon_window_class;
326 ModestWindowClass *modest_window_class;
327 GtkBindingSet *binding_set;
329 gobject_class = (GObjectClass*) klass;
330 hildon_window_class = (HildonWindowClass *) klass;
331 modest_window_class = (ModestWindowClass *) klass;
333 parent_class = g_type_class_peek_parent (klass);
334 gobject_class->finalize = modest_msg_view_window_finalize;
336 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
337 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
338 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
339 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
340 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
341 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
343 modest_window_class->save_state_func = save_state;
345 klass->scroll_child = modest_msg_view_window_scroll_child;
347 signals[MSG_CHANGED_SIGNAL] =
348 g_signal_new ("msg-changed",
349 G_TYPE_FROM_CLASS (gobject_class),
351 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
353 modest_marshal_VOID__POINTER_POINTER,
354 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
356 signals[SCROLL_CHILD_SIGNAL] =
357 g_signal_new ("scroll-child",
358 G_TYPE_FROM_CLASS (gobject_class),
359 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
360 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
362 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
363 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
365 binding_set = gtk_binding_set_by_class (klass);
366 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
367 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
368 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
369 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
370 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
371 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
373 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
377 static void modest_header_view_observer_init(
378 ModestHeaderViewObserverIface *iface_class)
380 iface_class->update_func = modest_msg_view_window_update_model_replaced;
384 modest_msg_view_window_init (ModestMsgViewWindow *obj)
386 ModestMsgViewWindowPrivate *priv;
387 ModestWindowPrivate *parent_priv = NULL;
388 GtkActionGroup *action_group = NULL;
389 GError *error = NULL;
390 GdkPixbuf *window_icon;
392 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
393 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
394 parent_priv->ui_manager = gtk_ui_manager_new();
396 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
397 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
399 /* Add common actions */
400 gtk_action_group_add_actions (action_group,
401 modest_action_entries,
402 G_N_ELEMENTS (modest_action_entries),
404 gtk_action_group_add_toggle_actions (action_group,
405 msg_view_toggle_action_entries,
406 G_N_ELEMENTS (msg_view_toggle_action_entries),
409 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
410 g_object_unref (action_group);
412 /* Load the UI definition */
413 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
416 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
417 g_error_free (error);
422 /* Add accelerators */
423 gtk_window_add_accel_group (GTK_WINDOW (obj),
424 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
426 priv->is_search_result = FALSE;
427 priv->is_outbox = FALSE;
429 priv->msg_view = NULL;
430 priv->header_model = NULL;
431 priv->header_folder_id = NULL;
432 priv->clipboard_change_handler = 0;
433 priv->queue_change_handler = 0;
434 priv->account_removed_handler = 0;
435 priv->row_changed_handler = 0;
436 priv->row_deleted_handler = 0;
437 priv->row_inserted_handler = 0;
438 priv->rows_reordered_handler = 0;
439 priv->progress_hint = FALSE;
441 priv->optimized_view = FALSE;
442 priv->purge_timeout = 0;
443 priv->remove_attachment_banner = NULL;
444 priv->msg_uid = NULL;
446 priv->sighandlers = NULL;
449 init_window (MODEST_MSG_VIEW_WINDOW(obj));
451 /* Set window icon */
452 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
454 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
455 g_object_unref (window_icon);
458 hildon_program_add_window (hildon_program_get_instance(),
465 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
467 ModestMsgViewWindowPrivate *priv = NULL;
469 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
473 set_progress_hint (self, TRUE);
479 set_progress_hint (ModestMsgViewWindow *self,
482 ModestWindowPrivate *parent_priv;
483 ModestMsgViewWindowPrivate *priv;
485 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
487 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
490 /* Sets current progress hint */
491 priv->progress_hint = enabled;
493 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
499 init_window (ModestMsgViewWindow *obj)
501 GtkWidget *main_vbox;
502 ModestMsgViewWindowPrivate *priv;
504 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
506 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
507 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
508 main_vbox = gtk_vbox_new (FALSE, 6);
509 #ifdef MODEST_TOOLKIT_HILDON2
510 priv->main_scroll = hildon_pannable_area_new ();
511 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
513 #ifdef MODEST_USE_MOZEMBED
514 priv->main_scroll = priv->msg_view;
515 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
517 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
518 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
520 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
521 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
522 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
525 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
526 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
528 priv->find_toolbar = hildon_find_toolbar_new (NULL);
529 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
530 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
532 gtk_widget_show_all (GTK_WIDGET(main_vbox));
536 modest_msg_view_window_disconnect_signals (ModestWindow *self)
538 ModestMsgViewWindowPrivate *priv;
539 GtkWidget *header_view = NULL;
540 GtkWindow *parent_window = NULL;
542 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
544 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
545 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
546 priv->clipboard_change_handler))
547 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
548 priv->clipboard_change_handler);
550 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
551 priv->queue_change_handler))
552 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
553 priv->queue_change_handler);
555 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
556 priv->account_removed_handler))
557 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
558 priv->account_removed_handler);
560 if (priv->header_model) {
561 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
562 priv->row_changed_handler))
563 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
564 priv->row_changed_handler);
566 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
567 priv->row_deleted_handler))
568 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
569 priv->row_deleted_handler);
571 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
572 priv->row_inserted_handler))
573 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
574 priv->row_inserted_handler);
576 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
577 priv->rows_reordered_handler))
578 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
579 priv->rows_reordered_handler);
582 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
583 priv->sighandlers = NULL;
585 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
586 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
587 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
589 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
590 MODEST_HEADER_VIEW_OBSERVER(self));
596 modest_msg_view_window_finalize (GObject *obj)
598 ModestMsgViewWindowPrivate *priv;
600 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
602 /* Sanity check: shouldn't be needed, the window mgr should
603 call this function before */
604 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
606 if (priv->header_model != NULL) {
607 g_object_unref (priv->header_model);
608 priv->header_model = NULL;
611 if (priv->remove_attachment_banner) {
612 gtk_widget_destroy (priv->remove_attachment_banner);
613 g_object_unref (priv->remove_attachment_banner);
614 priv->remove_attachment_banner = NULL;
617 if (priv->purge_timeout > 0) {
618 g_source_remove (priv->purge_timeout);
619 priv->purge_timeout = 0;
622 if (priv->row_reference) {
623 gtk_tree_row_reference_free (priv->row_reference);
624 priv->row_reference = NULL;
627 if (priv->next_row_reference) {
628 gtk_tree_row_reference_free (priv->next_row_reference);
629 priv->next_row_reference = NULL;
633 g_free (priv->msg_uid);
634 priv->msg_uid = NULL;
637 G_OBJECT_CLASS(parent_class)->finalize (obj);
641 select_next_valid_row (GtkTreeModel *model,
642 GtkTreeRowReference **row_reference,
646 GtkTreeIter tmp_iter;
648 GtkTreePath *next = NULL;
649 gboolean retval = FALSE, finished;
651 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
653 path = gtk_tree_row_reference_get_path (*row_reference);
654 gtk_tree_model_get_iter (model, &tmp_iter, path);
655 gtk_tree_row_reference_free (*row_reference);
656 *row_reference = NULL;
660 TnyHeader *header = NULL;
662 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
663 gtk_tree_model_get (model, &tmp_iter,
664 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
668 if (msg_is_visible (header, is_outbox)) {
669 next = gtk_tree_model_get_path (model, &tmp_iter);
670 *row_reference = gtk_tree_row_reference_new (model, next);
671 gtk_tree_path_free (next);
675 g_object_unref (header);
678 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
679 next = gtk_tree_model_get_path (model, &tmp_iter);
681 /* Ensure that we are not selecting the same */
682 if (gtk_tree_path_compare (path, next) != 0) {
683 gtk_tree_model_get (model, &tmp_iter,
684 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
687 if (msg_is_visible (header, is_outbox)) {
688 *row_reference = gtk_tree_row_reference_new (model, next);
692 g_object_unref (header);
696 /* If we ended up in the same message
697 then there is no valid next
701 gtk_tree_path_free (next);
703 /* If there are no more messages and we don't
704 want to start again in the first one then
705 there is no valid next message */
711 gtk_tree_path_free (path);
716 /* TODO: This should be in _init(), with the parameters as properties. */
718 modest_msg_view_window_construct (ModestMsgViewWindow *self,
719 const gchar *modest_account_name,
720 const gchar *msg_uid)
723 ModestMsgViewWindowPrivate *priv = NULL;
724 ModestWindowPrivate *parent_priv = NULL;
725 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
726 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
728 obj = G_OBJECT (self);
729 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
730 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
732 priv->msg_uid = g_strdup (msg_uid);
735 parent_priv->menubar = NULL;
737 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
738 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
741 /* Add common dimming rules */
742 modest_dimming_rules_group_add_rules (toolbar_rules_group,
743 modest_msg_view_toolbar_dimming_entries,
744 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
745 MODEST_WINDOW (self));
746 modest_dimming_rules_group_add_rules (clipboard_rules_group,
747 modest_msg_view_clipboard_dimming_entries,
748 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
749 MODEST_WINDOW (self));
751 /* Insert dimming rules group for this window */
752 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
753 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
754 g_object_unref (toolbar_rules_group);
755 g_object_unref (clipboard_rules_group);
757 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
759 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);
760 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
761 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
762 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
763 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
764 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
765 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
766 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
767 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
768 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
769 G_CALLBACK (modest_ui_actions_on_details), obj);
770 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
771 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
772 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
773 G_CALLBACK (on_fetch_image), obj);
775 g_signal_connect (G_OBJECT (obj), "key-release-event",
776 G_CALLBACK (modest_msg_view_window_key_event),
779 g_signal_connect (G_OBJECT (obj), "key-press-event",
780 G_CALLBACK (modest_msg_view_window_key_event),
783 g_signal_connect (G_OBJECT (obj), "move-focus",
784 G_CALLBACK (on_move_focus), obj);
786 /* Mail Operation Queue */
787 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
789 G_CALLBACK (on_queue_changed),
792 /* Account manager */
793 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
795 G_CALLBACK(on_account_removed),
798 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
800 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
801 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
802 priv->last_search = NULL;
804 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
806 /* Init the clipboard actions dim status */
807 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
809 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
814 /* FIXME: parameter checks */
816 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
817 const gchar *modest_account_name,
818 const gchar *msg_uid,
820 GtkTreeRowReference *row_reference)
822 ModestMsgViewWindow *window = NULL;
823 ModestMsgViewWindowPrivate *priv = NULL;
824 TnyFolder *header_folder = NULL;
825 ModestHeaderView *header_view = NULL;
826 ModestWindow *main_window = NULL;
827 ModestWindowMgr *mgr = NULL;
830 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
833 mgr = modest_runtime_get_window_mgr ();
834 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
835 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
837 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
839 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
841 /* Remember the message list's TreeModel so we can detect changes
842 * and change the list selection when necessary: */
844 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
846 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
847 MODEST_MAIN_WINDOW(main_window),
848 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
851 if (header_view != NULL){
852 header_folder = modest_header_view_get_folder(header_view);
853 /* This could happen if the header folder was
854 unseleted before opening this msg window (for
855 example if the user selects an account in the
856 folder view of the main window */
858 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
859 priv->header_folder_id = tny_folder_get_id(header_folder);
860 g_assert(priv->header_folder_id != NULL);
861 g_object_unref(header_folder);
865 /* Setup row references and connect signals */
866 priv->header_model = g_object_ref (model);
869 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
870 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
871 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
873 priv->row_reference = NULL;
874 priv->next_row_reference = NULL;
877 /* Connect signals */
878 priv->row_changed_handler =
879 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
880 G_CALLBACK(modest_msg_view_window_on_row_changed),
882 priv->row_deleted_handler =
883 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
884 G_CALLBACK(modest_msg_view_window_on_row_deleted),
886 priv->row_inserted_handler =
887 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
888 G_CALLBACK(modest_msg_view_window_on_row_inserted),
890 priv->rows_reordered_handler =
891 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
892 G_CALLBACK(modest_msg_view_window_on_row_reordered),
895 if (header_view != NULL){
896 modest_header_view_add_observer(header_view,
897 MODEST_HEADER_VIEW_OBSERVER(window));
900 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
901 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
903 /* gtk_widget_show_all (GTK_WIDGET (window)); */
904 modest_msg_view_window_update_priority (window);
905 /* Check dimming rules */
906 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
907 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
908 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
910 return MODEST_WINDOW(window);
914 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
915 const gchar *modest_account_name,
916 const gchar *msg_uid,
917 GtkTreeRowReference *row_reference)
919 ModestMsgViewWindow *window = NULL;
920 ModestMsgViewWindowPrivate *priv = NULL;
921 TnyFolder *header_folder = NULL;
922 ModestWindowMgr *mgr = NULL;
926 mgr = modest_runtime_get_window_mgr ();
927 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
928 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
930 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
932 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
934 /* Remember the message list's TreeModel so we can detect changes
935 * and change the list selection when necessary: */
937 if (header_view != NULL){
938 header_folder = modest_header_view_get_folder(header_view);
939 /* This could happen if the header folder was
940 unseleted before opening this msg window (for
941 example if the user selects an account in the
942 folder view of the main window */
944 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
945 priv->header_folder_id = tny_folder_get_id(header_folder);
946 g_assert(priv->header_folder_id != NULL);
947 g_object_unref(header_folder);
951 /* Setup row references and connect signals */
952 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
953 g_object_ref (priv->header_model);
956 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
957 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
958 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
960 priv->row_reference = NULL;
961 priv->next_row_reference = NULL;
964 /* Connect signals */
965 priv->row_changed_handler =
966 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
967 G_CALLBACK(modest_msg_view_window_on_row_changed),
969 priv->row_deleted_handler =
970 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
971 G_CALLBACK(modest_msg_view_window_on_row_deleted),
973 priv->row_inserted_handler =
974 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
975 G_CALLBACK(modest_msg_view_window_on_row_inserted),
977 priv->rows_reordered_handler =
978 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
979 G_CALLBACK(modest_msg_view_window_on_row_reordered),
982 if (header_view != NULL){
983 modest_header_view_add_observer(header_view,
984 MODEST_HEADER_VIEW_OBSERVER(window));
987 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
989 path = gtk_tree_row_reference_get_path (row_reference);
990 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
992 gtk_tree_model_get (priv->header_model, &iter,
993 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
995 message_reader (window, priv, header, row_reference);
997 gtk_tree_path_free (path);
999 /* Check dimming rules */
1000 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1001 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1002 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1004 return MODEST_WINDOW(window);
1008 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1009 const gchar *modest_account_name,
1010 const gchar *msg_uid)
1012 ModestMsgViewWindow *window = NULL;
1013 ModestMsgViewWindowPrivate *priv = NULL;
1014 ModestWindowMgr *mgr = NULL;
1016 mgr = modest_runtime_get_window_mgr ();
1017 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1018 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1019 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1021 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1023 /* Remember that this is a search result,
1024 * so we can disable some UI appropriately: */
1025 priv->is_search_result = TRUE;
1027 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1029 update_window_title (window);
1030 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1031 modest_msg_view_window_update_priority (window);
1033 /* Check dimming rules */
1034 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1035 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1036 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1038 return MODEST_WINDOW(window);
1042 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1043 const gchar *modest_account_name,
1044 const gchar *msg_uid)
1046 GObject *obj = NULL;
1047 ModestMsgViewWindowPrivate *priv;
1048 ModestWindowMgr *mgr = NULL;
1050 g_return_val_if_fail (msg, NULL);
1051 mgr = modest_runtime_get_window_mgr ();
1052 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1053 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1054 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1055 modest_account_name, msg_uid);
1057 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1058 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1060 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1062 /* Check dimming rules */
1063 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1064 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1065 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1067 return MODEST_WINDOW(obj);
1071 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1074 ModestMsgViewWindow *window)
1076 check_dimming_rules_after_change (window);
1080 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1082 ModestMsgViewWindow *window)
1084 check_dimming_rules_after_change (window);
1086 /* The window could have dissapeared */
1089 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1091 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1092 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1096 /* On insertions we check if the folder still has the message we are
1097 * showing or do not. If do not, we do nothing. Which means we are still
1098 * not attached to any header folder and thus next/prev buttons are
1099 * still dimmed. Once the message that is shown by msg-view is found, the
1100 * new model of header-view will be attached and the references will be set.
1101 * On each further insertions dimming rules will be checked. However
1102 * this requires extra CPU time at least works.
1103 * (An message might be deleted from TnyFolder and thus will not be
1104 * inserted into the model again for example if it is removed by the
1105 * imap server and the header view is refreshed.)
1108 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1109 GtkTreePath *tree_path,
1110 GtkTreeIter *tree_iter,
1111 ModestMsgViewWindow *window)
1113 ModestMsgViewWindowPrivate *priv = NULL;
1114 TnyHeader *header = NULL;
1116 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1117 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1119 g_assert (model == priv->header_model);
1121 /* Check if the newly inserted message is the same we are actually
1122 * showing. IF not, we should remain detached from the header model
1123 * and thus prev and next toolbar buttons should remain dimmed. */
1124 gtk_tree_model_get (model, tree_iter,
1125 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1128 if (TNY_IS_HEADER (header)) {
1131 uid = modest_tny_folder_get_header_unique_id (header);
1132 if (!g_str_equal(priv->msg_uid, uid)) {
1133 check_dimming_rules_after_change (window);
1135 g_object_unref (G_OBJECT(header));
1139 g_object_unref(G_OBJECT(header));
1142 if (priv->row_reference) {
1143 gtk_tree_row_reference_free (priv->row_reference);
1146 /* Setup row_reference for the actual msg. */
1147 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1148 if (priv->row_reference == NULL) {
1149 g_warning("No reference for msg header item.");
1153 /* Now set up next_row_reference. */
1154 if (priv->next_row_reference) {
1155 gtk_tree_row_reference_free (priv->next_row_reference);
1158 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1159 select_next_valid_row (priv->header_model,
1160 &(priv->next_row_reference), FALSE, priv->is_outbox);
1162 /* Connect the remaining callbacks to become able to detect
1163 * changes in header-view. */
1164 priv->row_changed_handler =
1165 g_signal_connect (priv->header_model, "row-changed",
1166 G_CALLBACK (modest_msg_view_window_on_row_changed),
1168 priv->row_deleted_handler =
1169 g_signal_connect (priv->header_model, "row-deleted",
1170 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1172 priv->rows_reordered_handler =
1173 g_signal_connect (priv->header_model, "rows-reordered",
1174 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1177 check_dimming_rules_after_change (window);
1181 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1185 ModestMsgViewWindow *window)
1187 ModestMsgViewWindowPrivate *priv = NULL;
1188 gboolean already_changed = FALSE;
1190 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1192 /* If the current row was reordered select the proper next
1193 valid row. The same if the next row reference changes */
1194 if (priv->row_reference &&
1195 gtk_tree_row_reference_valid (priv->row_reference)) {
1197 path = gtk_tree_row_reference_get_path (priv->row_reference);
1198 if (gtk_tree_path_compare (path, arg1) == 0) {
1199 if (priv->next_row_reference) {
1200 gtk_tree_row_reference_free (priv->next_row_reference);
1202 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1203 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1204 already_changed = TRUE;
1206 gtk_tree_path_free (path);
1208 if (!already_changed &&
1209 priv->next_row_reference &&
1210 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1212 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1213 if (gtk_tree_path_compare (path, arg1) == 0) {
1214 if (priv->next_row_reference) {
1215 gtk_tree_row_reference_free (priv->next_row_reference);
1217 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1218 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1220 gtk_tree_path_free (path);
1222 check_dimming_rules_after_change (window);
1225 /* The modest_msg_view_window_update_model_replaced implements update
1226 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1227 * actually belongs to the header-view is the same as the TnyFolder of
1228 * the message of msg-view or not. If they are different, there is
1229 * nothing to do. If they are the same, then the model has replaced and
1230 * the reference in msg-view shall be replaced from the old model to
1231 * the new model. In this case the view will be detached from it's
1232 * header folder. From this point the next/prev buttons are dimmed.
1235 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1236 GtkTreeModel *model,
1237 const gchar *tny_folder_id)
1239 ModestMsgViewWindowPrivate *priv = NULL;
1240 ModestMsgViewWindow *window = NULL;
1242 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1243 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1245 window = MODEST_MSG_VIEW_WINDOW(observer);
1246 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1248 /* If there is an other folder in the header-view then we do
1249 * not care about it's model (msg list). Else if the
1250 * header-view shows the folder the msg shown by us is in, we
1251 * shall replace our model reference and make some check. */
1252 if(model == NULL || tny_folder_id == NULL ||
1253 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1256 /* Model is changed(replaced), so we should forget the old
1257 * one. Because there might be other references and there
1258 * might be some change on the model even if we unreferenced
1259 * it, we need to disconnect our signals here. */
1260 if (priv->header_model) {
1261 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1262 priv->row_changed_handler))
1263 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1264 priv->row_changed_handler);
1265 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1266 priv->row_deleted_handler))
1267 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1268 priv->row_deleted_handler);
1269 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1270 priv->row_inserted_handler))
1271 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1272 priv->row_inserted_handler);
1273 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1274 priv->rows_reordered_handler))
1275 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1276 priv->rows_reordered_handler);
1279 if (priv->row_reference)
1280 gtk_tree_row_reference_free (priv->row_reference);
1281 if (priv->next_row_reference)
1282 gtk_tree_row_reference_free (priv->next_row_reference);
1283 g_object_unref(priv->header_model);
1286 priv->row_changed_handler = 0;
1287 priv->row_deleted_handler = 0;
1288 priv->row_inserted_handler = 0;
1289 priv->rows_reordered_handler = 0;
1290 priv->next_row_reference = NULL;
1291 priv->row_reference = NULL;
1292 priv->header_model = NULL;
1295 priv->header_model = g_object_ref (model);
1297 /* Also we must connect to the new model for row insertions.
1298 * Only for insertions now. We will need other ones only after
1299 * the msg is show by msg-view is added to the new model. */
1300 priv->row_inserted_handler =
1301 g_signal_connect (priv->header_model, "row-inserted",
1302 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1305 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1306 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1310 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1312 ModestMsgViewWindowPrivate *priv= NULL;
1314 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1315 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1317 return priv->progress_hint;
1321 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1323 ModestMsgViewWindowPrivate *priv= NULL;
1325 TnyHeader *header = NULL;
1326 GtkTreePath *path = NULL;
1329 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1330 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1332 /* If the message was not obtained from a treemodel,
1333 * for instance if it was opened directly by the search UI:
1335 if (priv->header_model == NULL ||
1336 priv->row_reference == NULL ||
1337 !gtk_tree_row_reference_valid (priv->row_reference)) {
1338 msg = modest_msg_view_window_get_message (self);
1340 header = tny_msg_get_header (msg);
1341 g_object_unref (msg);
1346 /* Get iter of the currently selected message in the header view: */
1347 path = gtk_tree_row_reference_get_path (priv->row_reference);
1348 g_return_val_if_fail (path != NULL, NULL);
1349 gtk_tree_model_get_iter (priv->header_model,
1353 /* Get current message header */
1354 gtk_tree_model_get (priv->header_model, &iter,
1355 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1358 gtk_tree_path_free (path);
1363 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1365 ModestMsgViewWindowPrivate *priv;
1367 g_return_val_if_fail (self, NULL);
1369 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1371 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1375 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1377 ModestMsgViewWindowPrivate *priv;
1379 g_return_val_if_fail (self, NULL);
1381 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1383 return (const gchar*) priv->msg_uid;
1387 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1390 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1391 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1392 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1396 is_active = gtk_toggle_action_get_active (toggle);
1399 gtk_widget_show (priv->find_toolbar);
1400 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1402 gtk_widget_hide (priv->find_toolbar);
1403 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1406 /* update the toggle buttons status */
1407 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1409 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1414 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1415 ModestMsgViewWindow *obj)
1417 GtkToggleAction *toggle;
1418 ModestWindowPrivate *parent_priv;
1419 ModestMsgViewWindowPrivate *priv;
1421 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1422 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1424 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1425 gtk_toggle_action_set_active (toggle, FALSE);
1426 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1430 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1431 ModestMsgViewWindow *obj)
1433 gchar *current_search;
1434 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1436 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1437 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1441 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1443 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1444 g_free (current_search);
1445 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1449 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1451 g_free (priv->last_search);
1452 priv->last_search = g_strdup (current_search);
1453 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1456 hildon_banner_show_information (NULL, NULL,
1457 _HL("ckct_ib_find_no_matches"));
1458 g_free (priv->last_search);
1459 priv->last_search = NULL;
1461 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1462 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1465 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1466 hildon_banner_show_information (NULL, NULL,
1467 _HL("ckct_ib_find_search_complete"));
1468 g_free (priv->last_search);
1469 priv->last_search = NULL;
1471 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1472 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1476 g_free (current_search);
1481 modest_msg_view_window_set_zoom (ModestWindow *window,
1484 ModestMsgViewWindowPrivate *priv;
1485 ModestWindowPrivate *parent_priv;
1487 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1489 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1490 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1491 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1496 modest_msg_view_window_get_zoom (ModestWindow *window)
1498 ModestMsgViewWindowPrivate *priv;
1500 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1502 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1503 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1507 modest_msg_view_window_zoom_plus (ModestWindow *window)
1510 ModestMsgViewWindowPrivate *priv;
1514 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1515 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1517 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1519 if (zoom_level >= 2.0) {
1520 hildon_banner_show_information (NULL, NULL,
1521 _CS("ckct_ib_max_zoom_level_reached"));
1523 } else if (zoom_level >= 1.5) {
1525 } else if (zoom_level >= 1.2) {
1527 } else if (zoom_level >= 1.0) {
1529 } else if (zoom_level >= 0.8) {
1531 } else if (zoom_level >= 0.5) {
1537 /* set zoom level */
1538 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1539 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1540 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1541 g_free (banner_text);
1542 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1548 modest_msg_view_window_zoom_minus (ModestWindow *window)
1551 ModestMsgViewWindowPrivate *priv;
1555 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1556 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1558 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1560 if (zoom_level <= 0.5) {
1561 hildon_banner_show_information (NULL, NULL,
1562 _CS("ckct_ib_min_zoom_level_reached"));
1564 } else if (zoom_level <= 0.8) {
1566 } else if (zoom_level <= 1.0) {
1568 } else if (zoom_level <= 1.2) {
1570 } else if (zoom_level <= 1.5) {
1572 } else if (zoom_level <= 2.0) {
1578 /* set zoom level */
1579 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1580 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1581 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1582 g_free (banner_text);
1583 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1590 modest_msg_view_window_key_event (GtkWidget *window,
1596 focus = gtk_window_get_focus (GTK_WINDOW (window));
1598 /* for the find toolbar case */
1599 if (focus && GTK_IS_ENTRY (focus)) {
1600 if (event->keyval == GDK_BackSpace) {
1602 copy = gdk_event_copy ((GdkEvent *) event);
1603 gtk_widget_event (focus, copy);
1604 gdk_event_free (copy);
1609 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1610 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1611 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1612 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1613 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1614 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1615 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1616 /* gboolean return_value; */
1618 if (event->type == GDK_KEY_PRESS) {
1619 GtkScrollType scroll_type;
1621 switch (event->keyval) {
1624 scroll_type = GTK_SCROLL_STEP_UP; break;
1627 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1629 case GDK_KP_Page_Up:
1630 scroll_type = GTK_SCROLL_PAGE_UP; break;
1632 case GDK_KP_Page_Down:
1633 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1636 scroll_type = GTK_SCROLL_START; break;
1639 scroll_type = GTK_SCROLL_END; break;
1640 default: scroll_type = GTK_SCROLL_NONE;
1643 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1644 /* scroll_type, FALSE, &return_value); */
1655 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1658 ModestMsgViewWindowPrivate *priv;
1659 GtkTreeIter tmp_iter;
1660 gboolean is_last_selected;
1662 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1663 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1665 /*if no model (so no rows at all), then virtually we are the last*/
1666 if (!priv->header_model || !priv->row_reference)
1669 if (!gtk_tree_row_reference_valid (priv->row_reference))
1672 path = gtk_tree_row_reference_get_path (priv->row_reference);
1676 is_last_selected = TRUE;
1677 while (is_last_selected) {
1679 gtk_tree_path_next (path);
1680 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1682 gtk_tree_model_get (priv->header_model, &tmp_iter,
1683 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1686 if (msg_is_visible (header, priv->is_outbox))
1687 is_last_selected = FALSE;
1688 g_object_unref(G_OBJECT(header));
1691 gtk_tree_path_free (path);
1692 return is_last_selected;
1696 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1698 ModestMsgViewWindowPrivate *priv;
1700 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1701 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1703 return priv->header_model != NULL;
1707 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1709 ModestMsgViewWindowPrivate *priv;
1711 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1712 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1714 return priv->is_search_result;
1718 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1720 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1722 if (!check_outbox) {
1725 ModestTnySendQueueStatus status;
1726 status = modest_tny_all_send_queues_get_msg_status (header);
1727 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1728 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1733 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1736 ModestMsgViewWindowPrivate *priv;
1737 gboolean is_first_selected;
1738 GtkTreeIter tmp_iter;
1740 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1741 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1743 /*if no model (so no rows at all), then virtually we are the first*/
1744 if (!priv->header_model || !priv->row_reference)
1747 if (!gtk_tree_row_reference_valid (priv->row_reference))
1750 path = gtk_tree_row_reference_get_path (priv->row_reference);
1754 is_first_selected = TRUE;
1755 while (is_first_selected) {
1757 if(!gtk_tree_path_prev (path))
1759 /* Here the 'if' is needless for logic, but let make sure
1760 * iter is valid for gtk_tree_model_get. */
1761 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1763 gtk_tree_model_get (priv->header_model, &tmp_iter,
1764 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1767 if (msg_is_visible (header, priv->is_outbox))
1768 is_first_selected = FALSE;
1769 g_object_unref(G_OBJECT(header));
1772 gtk_tree_path_free (path);
1773 return is_first_selected;
1778 GtkTreeRowReference *row_reference;
1782 message_reader_performer (gboolean canceled,
1784 GtkWindow *parent_window,
1785 TnyAccount *account,
1788 ModestMailOperation *mail_op = NULL;
1789 MsgReaderInfo *info;
1791 info = (MsgReaderInfo *) user_data;
1792 if (canceled || err) {
1796 /* Register the header - it'll be unregistered in the callback */
1797 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1799 /* New mail operation */
1800 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1801 modest_ui_actions_disk_operations_error_handler,
1804 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1805 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1806 g_object_unref (mail_op);
1808 /* Update dimming rules */
1809 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1810 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1813 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1814 g_object_unref (info->header);
1815 g_slice_free (MsgReaderInfo, info);
1820 * Reads the message whose summary item is @header. It takes care of
1821 * several things, among others:
1823 * If the message was not previously downloaded then ask the user
1824 * before downloading. If there is no connection launch the connection
1825 * dialog. Update toolbar dimming rules.
1827 * Returns: TRUE if the mail operation was started, otherwise if the
1828 * user do not want to download the message, or if the user do not
1829 * want to connect, then the operation is not issued
1832 message_reader (ModestMsgViewWindow *window,
1833 ModestMsgViewWindowPrivate *priv,
1835 GtkTreeRowReference *row_reference)
1837 ModestWindowMgr *mgr;
1838 TnyAccount *account;
1840 MsgReaderInfo *info;
1842 g_return_val_if_fail (row_reference != NULL, FALSE);
1844 mgr = modest_runtime_get_window_mgr ();
1845 /* Msg download completed */
1846 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1848 /* We set the header from model while we're loading */
1849 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1850 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1852 /* Ask the user if he wants to download the message if
1854 if (!tny_device_is_online (modest_runtime_get_device())) {
1855 GtkResponseType response;
1857 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1858 _("mcen_nc_get_msg"));
1859 if (response == GTK_RESPONSE_CANCEL)
1862 folder = tny_header_get_folder (header);
1863 info = g_slice_new (MsgReaderInfo);
1864 info->header = g_object_ref (header);
1865 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1867 /* Offer the connection dialog if necessary */
1868 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1870 TNY_FOLDER_STORE (folder),
1871 message_reader_performer,
1873 g_object_unref (folder);
1878 folder = tny_header_get_folder (header);
1879 account = tny_folder_get_account (folder);
1880 info = g_slice_new (MsgReaderInfo);
1881 info->header = g_object_ref (header);
1882 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1884 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1885 g_object_unref (account);
1886 g_object_unref (folder);
1892 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1894 ModestMsgViewWindowPrivate *priv;
1895 GtkTreePath *path= NULL;
1896 GtkTreeIter tmp_iter;
1898 gboolean retval = TRUE;
1899 GtkTreeRowReference *row_reference = NULL;
1901 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1902 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1904 if (!priv->row_reference)
1907 /* Update the next row reference if it's not valid. This could
1908 happen if for example the header which it was pointing to,
1909 was deleted. The best place to do it is in the row-deleted
1910 handler but the tinymail model do not work like the glib
1911 tree models and reports the deletion when the row is still
1913 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1914 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1915 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1916 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1919 if (priv->next_row_reference)
1920 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1924 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1926 gtk_tree_model_get_iter (priv->header_model,
1929 gtk_tree_path_free (path);
1931 gtk_tree_model_get (priv->header_model, &tmp_iter,
1932 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1935 /* Read the message & show it */
1936 if (!message_reader (window, priv, header, row_reference)) {
1939 gtk_tree_row_reference_free (row_reference);
1942 g_object_unref (header);
1948 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1950 ModestMsgViewWindowPrivate *priv = NULL;
1952 gboolean finished = FALSE;
1953 gboolean retval = FALSE;
1955 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1956 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1958 /* Return inmediatly if there is no header model */
1959 if (!priv->header_model || !priv->row_reference)
1962 path = gtk_tree_row_reference_get_path (priv->row_reference);
1963 while (!finished && gtk_tree_path_prev (path)) {
1967 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1968 gtk_tree_model_get (priv->header_model, &iter,
1969 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1973 if (msg_is_visible (header, priv->is_outbox)) {
1974 GtkTreeRowReference *row_reference;
1975 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1976 /* Read the message & show it */
1977 retval = message_reader (window, priv, header, row_reference);
1978 gtk_tree_row_reference_free (row_reference);
1982 g_object_unref (header);
1986 gtk_tree_path_free (path);
1991 view_msg_cb (ModestMailOperation *mail_op,
1998 ModestMsgViewWindow *self = NULL;
1999 ModestMsgViewWindowPrivate *priv = NULL;
2000 GtkTreeRowReference *row_reference = NULL;
2002 /* Unregister the header (it was registered before creating the mail operation) */
2003 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2005 row_reference = (GtkTreeRowReference *) user_data;
2007 gtk_tree_row_reference_free (row_reference);
2011 /* If there was any error */
2012 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2013 gtk_tree_row_reference_free (row_reference);
2017 /* Get the window */
2018 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2019 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2020 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2022 /* Update the row reference */
2023 if (priv->row_reference != NULL) {
2024 gtk_tree_row_reference_free (priv->row_reference);
2025 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2026 if (priv->next_row_reference != NULL) {
2027 gtk_tree_row_reference_free (priv->next_row_reference);
2029 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2030 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2033 /* Mark header as read */
2034 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2035 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2037 /* Set new message */
2038 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2039 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2040 modest_msg_view_window_update_priority (self);
2041 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2042 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2045 /* Set the new message uid of the window */
2046 if (priv->msg_uid) {
2047 g_free (priv->msg_uid);
2048 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2051 /* Notify the observers */
2052 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2053 0, priv->header_model, priv->row_reference);
2056 g_object_unref (self);
2057 gtk_tree_row_reference_free (row_reference);
2061 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2063 ModestMsgViewWindowPrivate *priv;
2065 TnyFolderType folder_type;
2067 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2069 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2071 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2075 folder = tny_msg_get_folder (msg);
2077 folder_type = modest_tny_folder_guess_folder_type (folder);
2078 g_object_unref (folder);
2080 g_object_unref (msg);
2088 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2090 ModestMsgViewWindowPrivate *priv;
2091 TnyHeader *header = NULL;
2092 TnyHeaderFlags flags = 0;
2094 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2096 if (priv->header_model && priv->row_reference) {
2098 GtkTreePath *path = NULL;
2100 path = gtk_tree_row_reference_get_path (priv->row_reference);
2101 g_return_if_fail (path != NULL);
2102 gtk_tree_model_get_iter (priv->header_model,
2104 gtk_tree_row_reference_get_path (priv->row_reference));
2106 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2108 gtk_tree_path_free (path);
2111 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2113 header = tny_msg_get_header (msg);
2114 g_object_unref (msg);
2119 flags = tny_header_get_flags (header);
2120 g_object_unref(G_OBJECT(header));
2123 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2128 toolbar_resize (ModestMsgViewWindow *self)
2130 ModestMsgViewWindowPrivate *priv = NULL;
2131 ModestWindowPrivate *parent_priv = NULL;
2133 gint static_button_size;
2134 ModestWindowMgr *mgr;
2136 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2137 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2138 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2140 mgr = modest_runtime_get_window_mgr ();
2141 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2143 if (parent_priv->toolbar) {
2144 /* left size buttons */
2145 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2146 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2147 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2148 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2149 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2150 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2151 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2152 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2153 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2154 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2155 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2156 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2157 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2158 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2159 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2160 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2162 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2163 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2164 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2165 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2171 modest_msg_view_window_show_toolbar (ModestWindow *self,
2172 gboolean show_toolbar)
2174 ModestMsgViewWindowPrivate *priv = NULL;
2175 ModestWindowPrivate *parent_priv;
2176 GtkWidget *reply_button = NULL, *menu = NULL;
2178 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2179 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2181 /* Set optimized view status */
2182 priv->optimized_view = !show_toolbar;
2184 if (!parent_priv->toolbar) {
2185 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2187 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2189 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2190 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2191 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2194 hildon_window_add_toolbar (HILDON_WINDOW (self),
2195 GTK_TOOLBAR (parent_priv->toolbar));
2197 /* Set reply button tap and hold menu */
2198 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2199 "/ToolBar/ToolbarMessageReply");
2200 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2201 "/ToolbarReplyCSM");
2202 if (menu && reply_button)
2203 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2207 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2208 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2209 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2211 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2212 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2213 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2215 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2218 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2219 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2224 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2226 ModestMsgViewWindow *window)
2228 if (!GTK_WIDGET_VISIBLE (window))
2231 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2235 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2237 ModestMsgViewWindowPrivate *priv;
2239 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2240 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2242 return priv->progress_hint;
2246 observers_empty (ModestMsgViewWindow *self)
2249 ModestMsgViewWindowPrivate *priv;
2250 gboolean is_empty = TRUE;
2251 guint pending_ops = 0;
2253 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2254 tmp = priv->progress_widgets;
2256 /* Check all observers */
2257 while (tmp && is_empty) {
2258 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2259 is_empty = pending_ops == 0;
2261 tmp = g_slist_next(tmp);
2268 on_account_removed (TnyAccountStore *account_store,
2269 TnyAccount *account,
2272 /* Do nothing if it's a transport account, because we only
2273 show the messages of a store account */
2274 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2275 const gchar *parent_acc = NULL;
2276 const gchar *our_acc = NULL;
2278 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2279 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2281 /* Close this window if I'm showing a message of the removed account */
2282 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2283 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2288 on_mail_operation_started (ModestMailOperation *mail_op,
2291 ModestMsgViewWindow *self;
2292 ModestMailOperationTypeOperation op_type;
2294 ModestMsgViewWindowPrivate *priv;
2295 GObject *source = NULL;
2297 self = MODEST_MSG_VIEW_WINDOW (user_data);
2298 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2299 op_type = modest_mail_operation_get_type_operation (mail_op);
2300 tmp = priv->progress_widgets;
2301 source = modest_mail_operation_get_source(mail_op);
2302 if (G_OBJECT (self) == source) {
2303 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2304 set_toolbar_transfer_mode(self);
2306 modest_progress_object_add_operation (
2307 MODEST_PROGRESS_OBJECT (tmp->data),
2309 tmp = g_slist_next (tmp);
2313 g_object_unref (source);
2317 on_mail_operation_finished (ModestMailOperation *mail_op,
2320 ModestMsgViewWindow *self;
2321 ModestMailOperationTypeOperation op_type;
2323 ModestMsgViewWindowPrivate *priv;
2325 self = MODEST_MSG_VIEW_WINDOW (user_data);
2326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2327 op_type = modest_mail_operation_get_type_operation (mail_op);
2328 tmp = priv->progress_widgets;
2330 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2332 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2334 tmp = g_slist_next (tmp);
2337 /* If no more operations are being observed, NORMAL mode is enabled again */
2338 if (observers_empty (self)) {
2339 set_progress_hint (self, FALSE);
2342 /* Update dimming rules. We have to do this right here
2343 and not in view_msg_cb because at that point the
2344 transfer mode is still enabled so the dimming rule
2345 won't let the user delete the message that has been
2346 readed for example */
2347 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2348 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2353 on_queue_changed (ModestMailOperationQueue *queue,
2354 ModestMailOperation *mail_op,
2355 ModestMailOperationQueueNotification type,
2356 ModestMsgViewWindow *self)
2358 ModestMsgViewWindowPrivate *priv;
2360 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2362 /* If this operations was created by another window, do nothing */
2363 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2366 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2367 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2369 "operation-started",
2370 G_CALLBACK (on_mail_operation_started),
2372 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2374 "operation-finished",
2375 G_CALLBACK (on_mail_operation_finished),
2377 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2378 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2380 "operation-started");
2381 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2383 "operation-finished");
2388 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2390 ModestMsgViewWindowPrivate *priv;
2391 TnyList *selected_attachments = NULL;
2393 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2394 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2396 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2397 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2399 return selected_attachments;
2405 guint banner_idle_id;
2406 } DecodeAsyncHelper;
2409 decode_async_banner_idle (gpointer user_data)
2411 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2413 helper->banner_idle_id = 0;
2414 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2420 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2426 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2428 if (helper->banner_idle_id > 0) {
2429 g_source_remove (helper->banner_idle_id);
2430 helper->banner_idle_id = 0;
2432 if (helper->banner) {
2433 gtk_widget_destroy (helper->banner);
2434 helper->banner = NULL;
2436 if (cancelled || err) {
2437 modest_platform_information_banner (NULL, NULL,
2438 _("mail_ib_file_operation_failed"));
2442 /* make the file read-only */
2443 g_chmod(helper->filepath, 0444);
2445 /* Activate the file */
2446 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2450 g_free (helper->filepath);
2451 g_slice_free (DecodeAsyncHelper, helper);
2455 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2456 TnyMimePart *mime_part)
2458 ModestMsgViewWindowPrivate *priv;
2459 const gchar *msg_uid;
2460 gchar *attachment_uid = NULL;
2461 gint attachment_index = 0;
2462 TnyList *attachments;
2464 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2465 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2466 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2468 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2469 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2470 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2471 g_object_unref (attachments);
2473 if (msg_uid && attachment_index >= 0) {
2474 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2477 if (mime_part == NULL) {
2478 gboolean error = FALSE;
2479 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2480 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2482 } else if (tny_list_get_length (selected_attachments) > 1) {
2483 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2487 iter = tny_list_create_iterator (selected_attachments);
2488 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2489 g_object_unref (iter);
2491 g_object_unref (selected_attachments);
2496 g_object_ref (mime_part);
2499 if (tny_mime_part_is_purged (mime_part)) {
2500 g_object_unref (mime_part);
2504 if (!modest_tny_mime_part_is_msg (mime_part)) {
2505 gchar *filepath = NULL;
2506 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2507 gboolean show_error_banner = FALSE;
2508 TnyFsStream *temp_stream = NULL;
2509 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2512 if (temp_stream != NULL) {
2513 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2514 helper->filepath = g_strdup (filepath);
2515 helper->banner = NULL;
2516 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2517 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2518 on_decode_to_stream_async_handler,
2521 g_object_unref (temp_stream);
2522 /* NOTE: files in the temporary area will be automatically
2523 * cleaned after some time if they are no longer in use */
2526 const gchar *content_type;
2527 /* the file may already exist but it isn't writable,
2528 * let's try to open it anyway */
2529 content_type = tny_mime_part_get_content_type (mime_part);
2530 modest_platform_activate_file (filepath, content_type);
2532 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2533 show_error_banner = TRUE;
2538 if (show_error_banner)
2539 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2541 /* message attachment */
2542 TnyHeader *header = NULL;
2543 ModestWindowMgr *mgr;
2544 ModestWindow *msg_win = NULL;
2547 header = tny_msg_get_header (TNY_MSG (mime_part));
2548 mgr = modest_runtime_get_window_mgr ();
2549 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2552 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2553 * thus, we don't do anything */
2554 g_warning ("window for is already being created");
2556 /* it's not found, so create a new window for it */
2557 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2558 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2560 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2561 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2562 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2563 modest_window_get_zoom (MODEST_WINDOW (window)));
2564 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2565 gtk_widget_show_all (GTK_WIDGET (msg_win));
2568 g_object_unref (mime_part);
2581 GnomeVFSResult result;
2584 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2585 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2586 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2587 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2590 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2594 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2595 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2596 g_free (pair->filename);
2597 g_object_unref (pair->part);
2598 g_slice_free (SaveMimePartPair, pair);
2600 g_list_free (info->pairs);
2603 gtk_widget_destroy (info->banner);
2604 g_slice_free (SaveMimePartInfo, info);
2609 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2611 if (info->pairs != NULL) {
2612 save_mime_part_to_file (info);
2614 /* This is a GDK lock because we are an idle callback and
2615 * hildon_banner_show_information is or does Gtk+ code */
2617 gdk_threads_enter (); /* CHECKED */
2618 save_mime_part_info_free (info, TRUE);
2619 if (info->result == GNOME_VFS_OK) {
2620 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2621 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2622 hildon_banner_show_information (NULL, NULL,
2623 _KR("cerm_device_memory_full"));
2625 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2627 gdk_threads_leave (); /* CHECKED */
2634 save_mime_part_to_file (SaveMimePartInfo *info)
2636 GnomeVFSHandle *handle;
2638 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2640 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2641 if (info->result == GNOME_VFS_OK) {
2642 GError *error = NULL;
2643 stream = tny_vfs_stream_new (handle);
2644 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2645 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2647 if ((error->domain == TNY_ERROR_DOMAIN) &&
2648 (error->code = TNY_IO_ERROR_WRITE) &&
2649 (errno == ENOSPC)) {
2650 info->result = GNOME_VFS_ERROR_NO_SPACE;
2652 info->result = GNOME_VFS_ERROR_IO;
2655 g_object_unref (G_OBJECT (stream));
2656 g_object_unref (pair->part);
2657 g_slice_free (SaveMimePartPair, pair);
2658 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2660 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2661 save_mime_part_info_free (info, FALSE);
2664 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2669 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2671 gboolean is_ok = TRUE;
2672 gint replaced_files = 0;
2673 const GList *files = info->pairs;
2676 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2677 SaveMimePartPair *pair = iter->data;
2678 if (modest_utils_file_exists (pair->filename)) {
2682 if (replaced_files) {
2683 GtkWidget *confirm_overwrite_dialog;
2684 const gchar *message = (replaced_files == 1) ?
2685 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2686 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2687 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2690 gtk_widget_destroy (confirm_overwrite_dialog);
2694 save_mime_part_info_free (info, TRUE);
2696 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2697 _CS("sfil_ib_saving"));
2698 info->banner = banner;
2699 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2705 save_attachments_response (GtkDialog *dialog,
2709 TnyList *mime_parts;
2711 GList *files_to_save = NULL;
2713 mime_parts = TNY_LIST (user_data);
2715 if (arg1 != GTK_RESPONSE_OK)
2718 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2720 if (!modest_utils_folder_writable (chooser_uri)) {
2721 hildon_banner_show_information
2722 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2726 iter = tny_list_create_iterator (mime_parts);
2727 while (!tny_iterator_is_done (iter)) {
2728 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2730 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2731 !tny_mime_part_is_purged (mime_part) &&
2732 (tny_mime_part_get_filename (mime_part) != NULL)) {
2733 SaveMimePartPair *pair;
2735 pair = g_slice_new0 (SaveMimePartPair);
2737 if (tny_list_get_length (mime_parts) > 1) {
2739 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2740 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2743 pair->filename = g_strdup (chooser_uri);
2745 pair->part = mime_part;
2746 files_to_save = g_list_prepend (files_to_save, pair);
2748 tny_iterator_next (iter);
2750 g_object_unref (iter);
2752 g_free (chooser_uri);
2754 if (files_to_save != NULL) {
2755 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2756 info->pairs = files_to_save;
2757 info->result = TRUE;
2758 save_mime_parts_to_file_with_checks (info);
2762 /* Free and close the dialog */
2763 g_object_unref (mime_parts);
2764 gtk_widget_destroy (GTK_WIDGET (dialog));
2768 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2770 ModestMsgViewWindowPrivate *priv;
2771 GtkWidget *save_dialog = NULL;
2772 gchar *folder = NULL;
2773 gchar *filename = NULL;
2774 gchar *save_multiple_str = NULL;
2776 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2777 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2779 if (mime_parts == NULL) {
2780 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2781 * selection available */
2782 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2783 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2786 g_object_ref (mime_parts);
2789 /* prepare dialog */
2790 if (tny_list_get_length (mime_parts) == 1) {
2792 /* only one attachment selected */
2793 iter = tny_list_create_iterator (mime_parts);
2794 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2795 g_object_unref (iter);
2796 if (!modest_tny_mime_part_is_msg (mime_part) &&
2797 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2798 !tny_mime_part_is_purged (mime_part)) {
2799 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2801 /* TODO: show any error? */
2802 g_warning ("Tried to save a non-file attachment");
2803 g_object_unref (mime_parts);
2806 g_object_unref (mime_part);
2808 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2809 tny_list_get_length (mime_parts));
2812 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2813 GTK_FILE_CHOOSER_ACTION_SAVE);
2816 folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2817 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2822 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2827 /* if multiple, set multiple string */
2828 if (save_multiple_str) {
2829 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2830 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2833 /* We must run this asynchronously, because the hildon dialog
2834 performs a gtk_dialog_run by itself which leads to gdk
2836 g_signal_connect (save_dialog, "response",
2837 G_CALLBACK (save_attachments_response), mime_parts);
2839 gtk_widget_show_all (save_dialog);
2843 show_remove_attachment_information (gpointer userdata)
2845 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2846 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2848 /* We're outside the main lock */
2849 gdk_threads_enter ();
2851 if (priv->remove_attachment_banner != NULL) {
2852 gtk_widget_destroy (priv->remove_attachment_banner);
2853 g_object_unref (priv->remove_attachment_banner);
2856 priv->remove_attachment_banner = g_object_ref (
2857 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2859 gdk_threads_leave ();
2865 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2867 ModestMsgViewWindowPrivate *priv;
2868 TnyList *mime_parts = NULL;
2869 gchar *confirmation_message;
2875 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2876 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2878 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2879 * because we don't have selection
2881 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2883 /* Remove already purged messages from mime parts list */
2884 iter = tny_list_create_iterator (mime_parts);
2885 while (!tny_iterator_is_done (iter)) {
2886 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2887 tny_iterator_next (iter);
2888 if (tny_mime_part_is_purged (part)) {
2889 tny_list_remove (mime_parts, (GObject *) part);
2891 g_object_unref (part);
2893 g_object_unref (iter);
2895 if (tny_list_get_length (mime_parts) == 0) {
2896 g_object_unref (mime_parts);
2900 n_attachments = tny_list_get_length (mime_parts);
2901 if (n_attachments == 1) {
2905 iter = tny_list_create_iterator (mime_parts);
2906 part = (TnyMimePart *) tny_iterator_get_current (iter);
2907 g_object_unref (iter);
2908 if (modest_tny_mime_part_is_msg (part)) {
2910 header = tny_msg_get_header (TNY_MSG (part));
2911 filename = tny_header_dup_subject (header);
2912 g_object_unref (header);
2913 if (filename == NULL)
2914 filename = g_strdup (_("mail_va_no_subject"));
2916 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2918 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2920 g_object_unref (part);
2922 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2923 "mcen_nc_purge_files_text",
2924 n_attachments), n_attachments);
2926 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2927 confirmation_message);
2928 g_free (confirmation_message);
2930 if (response != GTK_RESPONSE_OK) {
2931 g_object_unref (mime_parts);
2935 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2937 iter = tny_list_create_iterator (mime_parts);
2938 while (!tny_iterator_is_done (iter)) {
2941 part = (TnyMimePart *) tny_iterator_get_current (iter);
2942 tny_mime_part_set_purged (TNY_MIME_PART (part));
2943 g_object_unref (part);
2944 tny_iterator_next (iter);
2946 g_object_unref (iter);
2948 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2949 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2950 tny_msg_rewrite_cache (msg);
2951 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2952 g_object_unref (msg);
2954 g_object_unref (mime_parts);
2956 if (priv->purge_timeout > 0) {
2957 g_source_remove (priv->purge_timeout);
2958 priv->purge_timeout = 0;
2961 if (priv->remove_attachment_banner) {
2962 gtk_widget_destroy (priv->remove_attachment_banner);
2963 g_object_unref (priv->remove_attachment_banner);
2964 priv->remove_attachment_banner = NULL;
2972 update_window_title (ModestMsgViewWindow *window)
2974 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2976 TnyHeader *header = NULL;
2977 gchar *subject = NULL;
2979 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2982 header = tny_msg_get_header (msg);
2983 subject = tny_header_dup_subject (header);
2984 g_object_unref (header);
2985 g_object_unref (msg);
2988 if ((subject == NULL)||(subject[0] == '\0')) {
2990 subject = g_strdup (_("mail_va_no_subject"));
2993 gtk_window_set_title (GTK_WINDOW (window), subject);
2997 static void on_move_focus (GtkWidget *widget,
2998 GtkDirectionType direction,
3001 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3005 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3007 GnomeVFSResult result;
3008 GnomeVFSHandle *handle = NULL;
3009 GnomeVFSFileInfo *info = NULL;
3012 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3013 if (result != GNOME_VFS_OK) {
3018 info = gnome_vfs_file_info_new ();
3019 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3020 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3021 /* We put a "safe" default size for going to cache */
3022 *expected_size = (300*1024);
3024 *expected_size = info->size;
3026 gnome_vfs_file_info_unref (info);
3028 stream = tny_vfs_stream_new (handle);
3037 TnyStream *output_stream;
3038 GtkWidget *msg_view;
3042 on_fetch_image_idle_refresh_view (gpointer userdata)
3045 FetchImageData *fidata = (FetchImageData *) userdata;
3046 g_message ("REFRESH VIEW");
3047 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3048 g_message ("QUEUING DRAW");
3049 gtk_widget_queue_draw (fidata->msg_view);
3051 g_object_unref (fidata->msg_view);
3052 g_slice_free (FetchImageData, fidata);
3057 on_fetch_image_thread (gpointer userdata)
3059 FetchImageData *fidata = (FetchImageData *) userdata;
3060 TnyStreamCache *cache;
3061 TnyStream *cache_stream;
3063 cache = modest_runtime_get_images_cache ();
3064 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3065 g_free (fidata->cache_id);
3066 g_free (fidata->uri);
3068 if (cache_stream != NULL) {
3069 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3070 tny_stream_close (cache_stream);
3071 g_object_unref (cache_stream);
3074 tny_stream_close (fidata->output_stream);
3075 g_object_unref (fidata->output_stream);
3078 gdk_threads_enter ();
3079 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3080 gdk_threads_leave ();
3086 on_fetch_image (ModestMsgView *msgview,
3089 ModestMsgViewWindow *window)
3091 const gchar *current_account;
3092 ModestMsgViewWindowPrivate *priv;
3093 FetchImageData *fidata;
3095 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3097 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3099 fidata = g_slice_new0 (FetchImageData);
3100 fidata->msg_view = g_object_ref (msgview);
3101 fidata->uri = g_strdup (uri);
3102 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3103 fidata->output_stream = g_object_ref (stream);
3105 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3106 g_object_unref (fidata->output_stream);
3107 g_free (fidata->cache_id);
3108 g_free (fidata->uri);
3109 g_object_unref (fidata->msg_view);
3110 g_slice_free (FetchImageData, fidata);
3111 tny_stream_close (stream);
3119 setup_menu (ModestMsgViewWindow *self)
3121 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3123 /* Settings menu buttons */
3124 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3125 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3126 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3127 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3128 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3129 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3131 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3132 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3133 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3134 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3135 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3136 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3138 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3139 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3140 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3141 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3142 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3143 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3145 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3146 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3147 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3148 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3149 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3150 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3154 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3156 ModestMsgViewWindowPrivate *priv;
3157 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3158 GSList *recipients = NULL;
3160 gboolean contacts_to_add = FALSE;
3162 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3166 header = modest_msg_view_window_get_header (self);
3169 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3170 g_object_unref (header);
3172 recipients = modest_tny_msg_get_all_recipients_list (msg);
3173 g_object_unref (msg);
3176 if (recipients != NULL) {
3177 GtkWidget *picker_dialog;
3178 GtkWidget *selector;
3180 gchar *selected = NULL;
3182 selector = hildon_touch_selector_new_text ();
3183 g_object_ref (selector);
3185 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3186 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3187 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3188 (const gchar *) node->data);
3189 contacts_to_add = TRUE;
3193 if (contacts_to_add) {
3196 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3197 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3199 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3200 HILDON_TOUCH_SELECTOR (selector));
3202 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3204 if (picker_result == GTK_RESPONSE_OK) {
3205 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3207 gtk_widget_destroy (picker_dialog);
3210 modest_address_book_add_address (selected);
3215 g_object_unref (selector);
3220 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}