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);
225 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
229 /* list my signals */
236 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
237 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
241 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
242 MODEST_TYPE_MSG_VIEW_WINDOW, \
243 ModestMsgViewWindowPrivate))
245 static GtkWindowClass *parent_class = NULL;
247 /* uncomment the following if you have defined any signals */
248 static guint signals[LAST_SIGNAL] = {0};
251 modest_msg_view_window_get_type (void)
253 static GType my_type = 0;
255 static const GTypeInfo my_info = {
256 sizeof(ModestMsgViewWindowClass),
257 NULL, /* base init */
258 NULL, /* base finalize */
259 (GClassInitFunc) modest_msg_view_window_class_init,
260 NULL, /* class finalize */
261 NULL, /* class data */
262 sizeof(ModestMsgViewWindow),
264 (GInstanceInitFunc) modest_msg_view_window_init,
267 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
268 "ModestMsgViewWindow",
271 static const GInterfaceInfo modest_header_view_observer_info =
273 (GInterfaceInitFunc) modest_header_view_observer_init,
274 NULL, /* interface_finalize */
275 NULL /* interface_data */
278 g_type_add_interface_static (my_type,
279 MODEST_TYPE_HEADER_VIEW_OBSERVER,
280 &modest_header_view_observer_info);
286 save_state (ModestWindow *self)
288 modest_widget_memory_save (modest_runtime_get_conf (),
290 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
294 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
295 GtkScrollType scroll_type,
299 ModestMsgViewWindowPrivate *priv;
300 gboolean return_value;
302 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
303 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
308 add_scroll_binding (GtkBindingSet *binding_set,
310 GtkScrollType scroll)
312 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
314 gtk_binding_entry_add_signal (binding_set, keyval, 0,
316 GTK_TYPE_SCROLL_TYPE, scroll,
317 G_TYPE_BOOLEAN, FALSE);
318 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
320 GTK_TYPE_SCROLL_TYPE, scroll,
321 G_TYPE_BOOLEAN, FALSE);
325 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
327 GObjectClass *gobject_class;
328 HildonWindowClass *hildon_window_class;
329 ModestWindowClass *modest_window_class;
330 GtkBindingSet *binding_set;
332 gobject_class = (GObjectClass*) klass;
333 hildon_window_class = (HildonWindowClass *) klass;
334 modest_window_class = (ModestWindowClass *) klass;
336 parent_class = g_type_class_peek_parent (klass);
337 gobject_class->finalize = modest_msg_view_window_finalize;
339 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
340 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
341 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
342 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
343 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
344 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
346 modest_window_class->save_state_func = save_state;
348 klass->scroll_child = modest_msg_view_window_scroll_child;
350 signals[MSG_CHANGED_SIGNAL] =
351 g_signal_new ("msg-changed",
352 G_TYPE_FROM_CLASS (gobject_class),
354 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
356 modest_marshal_VOID__POINTER_POINTER,
357 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
359 signals[SCROLL_CHILD_SIGNAL] =
360 g_signal_new ("scroll-child",
361 G_TYPE_FROM_CLASS (gobject_class),
362 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
363 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
365 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
366 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
368 binding_set = gtk_binding_set_by_class (klass);
369 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
370 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
371 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
372 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
373 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
374 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
376 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
380 static void modest_header_view_observer_init(
381 ModestHeaderViewObserverIface *iface_class)
383 iface_class->update_func = modest_msg_view_window_update_model_replaced;
387 modest_msg_view_window_init (ModestMsgViewWindow *obj)
389 ModestMsgViewWindowPrivate *priv;
390 ModestWindowPrivate *parent_priv = NULL;
391 GtkActionGroup *action_group = NULL;
392 GError *error = NULL;
393 GdkPixbuf *window_icon;
395 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
396 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
397 parent_priv->ui_manager = gtk_ui_manager_new();
399 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
400 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
402 /* Add common actions */
403 gtk_action_group_add_actions (action_group,
404 modest_action_entries,
405 G_N_ELEMENTS (modest_action_entries),
407 gtk_action_group_add_toggle_actions (action_group,
408 msg_view_toggle_action_entries,
409 G_N_ELEMENTS (msg_view_toggle_action_entries),
412 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
413 g_object_unref (action_group);
415 /* Load the UI definition */
416 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
419 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
420 g_error_free (error);
425 /* Add accelerators */
426 gtk_window_add_accel_group (GTK_WINDOW (obj),
427 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
429 priv->is_search_result = FALSE;
430 priv->is_outbox = FALSE;
432 priv->msg_view = NULL;
433 priv->header_model = NULL;
434 priv->header_folder_id = NULL;
435 priv->clipboard_change_handler = 0;
436 priv->queue_change_handler = 0;
437 priv->account_removed_handler = 0;
438 priv->row_changed_handler = 0;
439 priv->row_deleted_handler = 0;
440 priv->row_inserted_handler = 0;
441 priv->rows_reordered_handler = 0;
442 priv->progress_hint = FALSE;
444 priv->optimized_view = FALSE;
445 priv->purge_timeout = 0;
446 priv->remove_attachment_banner = NULL;
447 priv->msg_uid = NULL;
449 priv->sighandlers = NULL;
452 init_window (MODEST_MSG_VIEW_WINDOW(obj));
454 /* Set window icon */
455 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
457 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
458 g_object_unref (window_icon);
461 hildon_program_add_window (hildon_program_get_instance(),
468 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
470 ModestMsgViewWindowPrivate *priv = NULL;
472 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
474 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
476 set_progress_hint (self, TRUE);
482 set_progress_hint (ModestMsgViewWindow *self,
485 ModestWindowPrivate *parent_priv;
486 ModestMsgViewWindowPrivate *priv;
488 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
490 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
491 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
493 /* Sets current progress hint */
494 priv->progress_hint = enabled;
496 if (GTK_WIDGET_VISIBLE (self)) {
497 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
504 init_window (ModestMsgViewWindow *obj)
506 GtkWidget *main_vbox;
507 ModestMsgViewWindowPrivate *priv;
509 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
511 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
512 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
513 main_vbox = gtk_vbox_new (FALSE, 6);
514 #ifdef MODEST_TOOLKIT_HILDON2
515 priv->main_scroll = hildon_pannable_area_new ();
516 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
518 #ifdef MODEST_USE_MOZEMBED
519 priv->main_scroll = priv->msg_view;
520 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
522 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
523 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
525 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
526 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
527 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
530 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
531 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
533 priv->find_toolbar = hildon_find_toolbar_new (NULL);
534 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
535 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
537 /* NULL-ize fields if the window is destroyed */
538 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
540 gtk_widget_show_all (GTK_WIDGET(main_vbox));
544 modest_msg_view_window_disconnect_signals (ModestWindow *self)
546 ModestMsgViewWindowPrivate *priv;
547 GtkWidget *header_view = NULL;
548 GtkWindow *parent_window = NULL;
550 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
552 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
553 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
554 priv->clipboard_change_handler))
555 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
556 priv->clipboard_change_handler);
558 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
559 priv->queue_change_handler))
560 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
561 priv->queue_change_handler);
563 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
564 priv->account_removed_handler))
565 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
566 priv->account_removed_handler);
568 if (priv->header_model) {
569 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
570 priv->row_changed_handler))
571 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
572 priv->row_changed_handler);
574 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
575 priv->row_deleted_handler))
576 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
577 priv->row_deleted_handler);
579 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
580 priv->row_inserted_handler))
581 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
582 priv->row_inserted_handler);
584 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
585 priv->rows_reordered_handler))
586 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
587 priv->rows_reordered_handler);
590 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
591 priv->sighandlers = NULL;
593 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
594 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
595 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
597 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
598 MODEST_HEADER_VIEW_OBSERVER(self));
604 modest_msg_view_window_finalize (GObject *obj)
606 ModestMsgViewWindowPrivate *priv;
608 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
610 /* Sanity check: shouldn't be needed, the window mgr should
611 call this function before */
612 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
614 if (priv->header_model != NULL) {
615 g_object_unref (priv->header_model);
616 priv->header_model = NULL;
619 if (priv->remove_attachment_banner) {
620 gtk_widget_destroy (priv->remove_attachment_banner);
621 g_object_unref (priv->remove_attachment_banner);
622 priv->remove_attachment_banner = NULL;
625 if (priv->purge_timeout > 0) {
626 g_source_remove (priv->purge_timeout);
627 priv->purge_timeout = 0;
630 if (priv->row_reference) {
631 gtk_tree_row_reference_free (priv->row_reference);
632 priv->row_reference = NULL;
635 if (priv->next_row_reference) {
636 gtk_tree_row_reference_free (priv->next_row_reference);
637 priv->next_row_reference = NULL;
641 g_free (priv->msg_uid);
642 priv->msg_uid = NULL;
645 G_OBJECT_CLASS(parent_class)->finalize (obj);
649 select_next_valid_row (GtkTreeModel *model,
650 GtkTreeRowReference **row_reference,
654 GtkTreeIter tmp_iter;
656 GtkTreePath *next = NULL;
657 gboolean retval = FALSE, finished;
659 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
661 path = gtk_tree_row_reference_get_path (*row_reference);
662 gtk_tree_model_get_iter (model, &tmp_iter, path);
663 gtk_tree_row_reference_free (*row_reference);
664 *row_reference = NULL;
668 TnyHeader *header = NULL;
670 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
671 gtk_tree_model_get (model, &tmp_iter,
672 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
676 if (msg_is_visible (header, is_outbox)) {
677 next = gtk_tree_model_get_path (model, &tmp_iter);
678 *row_reference = gtk_tree_row_reference_new (model, next);
679 gtk_tree_path_free (next);
683 g_object_unref (header);
686 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
687 next = gtk_tree_model_get_path (model, &tmp_iter);
689 /* Ensure that we are not selecting the same */
690 if (gtk_tree_path_compare (path, next) != 0) {
691 gtk_tree_model_get (model, &tmp_iter,
692 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
695 if (msg_is_visible (header, is_outbox)) {
696 *row_reference = gtk_tree_row_reference_new (model, next);
700 g_object_unref (header);
704 /* If we ended up in the same message
705 then there is no valid next
709 gtk_tree_path_free (next);
711 /* If there are no more messages and we don't
712 want to start again in the first one then
713 there is no valid next message */
719 gtk_tree_path_free (path);
724 /* TODO: This should be in _init(), with the parameters as properties. */
726 modest_msg_view_window_construct (ModestMsgViewWindow *self,
727 const gchar *modest_account_name,
728 const gchar *mailbox,
729 const gchar *msg_uid)
732 ModestMsgViewWindowPrivate *priv = NULL;
733 ModestWindowPrivate *parent_priv = NULL;
734 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
735 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
737 obj = G_OBJECT (self);
738 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
739 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
741 priv->msg_uid = g_strdup (msg_uid);
744 parent_priv->menubar = NULL;
746 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
747 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
750 /* Add common dimming rules */
751 modest_dimming_rules_group_add_rules (toolbar_rules_group,
752 modest_msg_view_toolbar_dimming_entries,
753 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
754 MODEST_WINDOW (self));
755 modest_dimming_rules_group_add_rules (clipboard_rules_group,
756 modest_msg_view_clipboard_dimming_entries,
757 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
758 MODEST_WINDOW (self));
760 /* Insert dimming rules group for this window */
761 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
762 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
763 g_object_unref (toolbar_rules_group);
764 g_object_unref (clipboard_rules_group);
766 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
768 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);
769 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
770 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
771 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
772 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
773 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
774 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
775 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
776 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
777 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
778 G_CALLBACK (modest_ui_actions_on_details), obj);
779 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
780 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
781 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
782 G_CALLBACK (on_fetch_image), obj);
784 g_signal_connect (G_OBJECT (obj), "key-release-event",
785 G_CALLBACK (modest_msg_view_window_key_event),
788 g_signal_connect (G_OBJECT (obj), "key-press-event",
789 G_CALLBACK (modest_msg_view_window_key_event),
792 g_signal_connect (G_OBJECT (obj), "move-focus",
793 G_CALLBACK (on_move_focus), obj);
795 g_signal_connect (G_OBJECT (obj), "map-event",
796 G_CALLBACK (_modest_msg_view_window_map_event),
799 /* Mail Operation Queue */
800 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
802 G_CALLBACK (on_queue_changed),
805 /* Account manager */
806 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
808 G_CALLBACK(on_account_removed),
811 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
812 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
814 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
815 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
816 priv->last_search = NULL;
818 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
820 /* Init the clipboard actions dim status */
821 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
823 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
828 /* FIXME: parameter checks */
830 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
831 const gchar *modest_account_name,
832 const gchar *mailbox,
833 const gchar *msg_uid,
835 GtkTreeRowReference *row_reference)
837 ModestMsgViewWindow *window = NULL;
838 ModestMsgViewWindowPrivate *priv = NULL;
839 TnyFolder *header_folder = NULL;
840 ModestHeaderView *header_view = NULL;
841 ModestWindow *main_window = NULL;
842 ModestWindowMgr *mgr = NULL;
845 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
848 mgr = modest_runtime_get_window_mgr ();
849 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
850 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
852 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
854 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
856 /* Remember the message list's TreeModel so we can detect changes
857 * and change the list selection when necessary: */
859 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
861 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
862 MODEST_MAIN_WINDOW(main_window),
863 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
866 if (header_view != NULL){
867 header_folder = modest_header_view_get_folder(header_view);
868 /* This could happen if the header folder was
869 unseleted before opening this msg window (for
870 example if the user selects an account in the
871 folder view of the main window */
873 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
874 priv->header_folder_id = tny_folder_get_id(header_folder);
875 g_assert(priv->header_folder_id != NULL);
876 g_object_unref(header_folder);
880 /* Setup row references and connect signals */
881 priv->header_model = g_object_ref (model);
884 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
885 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
886 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
888 priv->row_reference = NULL;
889 priv->next_row_reference = NULL;
892 /* Connect signals */
893 priv->row_changed_handler =
894 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
895 G_CALLBACK(modest_msg_view_window_on_row_changed),
897 priv->row_deleted_handler =
898 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
899 G_CALLBACK(modest_msg_view_window_on_row_deleted),
901 priv->row_inserted_handler =
902 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
903 G_CALLBACK(modest_msg_view_window_on_row_inserted),
905 priv->rows_reordered_handler =
906 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
907 G_CALLBACK(modest_msg_view_window_on_row_reordered),
910 if (header_view != NULL){
911 modest_header_view_add_observer(header_view,
912 MODEST_HEADER_VIEW_OBSERVER(window));
915 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
916 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
918 /* gtk_widget_show_all (GTK_WIDGET (window)); */
919 modest_msg_view_window_update_priority (window);
920 /* Check dimming rules */
921 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
922 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
923 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
925 return MODEST_WINDOW(window);
929 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
930 const gchar *modest_account_name,
931 const gchar *mailbox,
932 const gchar *msg_uid,
933 GtkTreeRowReference *row_reference)
935 ModestMsgViewWindow *window = NULL;
936 ModestMsgViewWindowPrivate *priv = NULL;
937 TnyFolder *header_folder = NULL;
938 ModestWindowMgr *mgr = NULL;
942 mgr = modest_runtime_get_window_mgr ();
943 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
944 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
946 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
948 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
950 /* Remember the message list's TreeModel so we can detect changes
951 * and change the list selection when necessary: */
953 if (header_view != NULL){
954 header_folder = modest_header_view_get_folder(header_view);
955 /* This could happen if the header folder was
956 unseleted before opening this msg window (for
957 example if the user selects an account in the
958 folder view of the main window */
960 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
961 priv->header_folder_id = tny_folder_get_id(header_folder);
962 g_assert(priv->header_folder_id != NULL);
963 g_object_unref(header_folder);
967 /* Setup row references and connect signals */
968 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
969 g_object_ref (priv->header_model);
972 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
973 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
974 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
976 priv->row_reference = NULL;
977 priv->next_row_reference = NULL;
980 /* Connect signals */
981 priv->row_changed_handler =
982 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
983 G_CALLBACK(modest_msg_view_window_on_row_changed),
985 priv->row_deleted_handler =
986 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
987 G_CALLBACK(modest_msg_view_window_on_row_deleted),
989 priv->row_inserted_handler =
990 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
991 G_CALLBACK(modest_msg_view_window_on_row_inserted),
993 priv->rows_reordered_handler =
994 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
995 G_CALLBACK(modest_msg_view_window_on_row_reordered),
998 if (header_view != NULL){
999 modest_header_view_add_observer(header_view,
1000 MODEST_HEADER_VIEW_OBSERVER(window));
1003 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1005 path = gtk_tree_row_reference_get_path (row_reference);
1006 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1008 gtk_tree_model_get (priv->header_model, &iter,
1009 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1011 message_reader (window, priv, header, row_reference);
1013 gtk_tree_path_free (path);
1015 /* Check dimming rules */
1016 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1017 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1018 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1020 return MODEST_WINDOW(window);
1024 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1025 const gchar *modest_account_name,
1026 const gchar *mailbox,
1027 const gchar *msg_uid)
1029 ModestMsgViewWindow *window = NULL;
1030 ModestMsgViewWindowPrivate *priv = NULL;
1031 ModestWindowMgr *mgr = NULL;
1033 mgr = modest_runtime_get_window_mgr ();
1034 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1035 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1036 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1038 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1040 /* Remember that this is a search result,
1041 * so we can disable some UI appropriately: */
1042 priv->is_search_result = TRUE;
1044 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1046 update_window_title (window);
1047 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1048 modest_msg_view_window_update_priority (window);
1050 /* Check dimming rules */
1051 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1052 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1053 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1055 return MODEST_WINDOW(window);
1059 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1060 const gchar *modest_account_name,
1061 const gchar *mailbox,
1062 const gchar *msg_uid)
1064 GObject *obj = NULL;
1065 ModestMsgViewWindowPrivate *priv;
1066 ModestWindowMgr *mgr = NULL;
1068 g_return_val_if_fail (msg, NULL);
1069 mgr = modest_runtime_get_window_mgr ();
1070 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1071 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1072 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1073 modest_account_name, mailbox, msg_uid);
1075 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1076 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1078 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1080 /* Check dimming rules */
1081 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1082 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1083 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1085 return MODEST_WINDOW(obj);
1089 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1092 ModestMsgViewWindow *window)
1094 check_dimming_rules_after_change (window);
1098 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1100 ModestMsgViewWindow *window)
1102 check_dimming_rules_after_change (window);
1104 /* The window could have dissapeared */
1107 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1109 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1110 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1114 /* On insertions we check if the folder still has the message we are
1115 * showing or do not. If do not, we do nothing. Which means we are still
1116 * not attached to any header folder and thus next/prev buttons are
1117 * still dimmed. Once the message that is shown by msg-view is found, the
1118 * new model of header-view will be attached and the references will be set.
1119 * On each further insertions dimming rules will be checked. However
1120 * this requires extra CPU time at least works.
1121 * (An message might be deleted from TnyFolder and thus will not be
1122 * inserted into the model again for example if it is removed by the
1123 * imap server and the header view is refreshed.)
1126 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1127 GtkTreePath *tree_path,
1128 GtkTreeIter *tree_iter,
1129 ModestMsgViewWindow *window)
1131 ModestMsgViewWindowPrivate *priv = NULL;
1132 TnyHeader *header = NULL;
1134 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1135 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1137 g_assert (model == priv->header_model);
1139 /* Check if the newly inserted message is the same we are actually
1140 * showing. IF not, we should remain detached from the header model
1141 * and thus prev and next toolbar buttons should remain dimmed. */
1142 gtk_tree_model_get (model, tree_iter,
1143 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1146 if (TNY_IS_HEADER (header)) {
1149 uid = modest_tny_folder_get_header_unique_id (header);
1150 if (!g_str_equal(priv->msg_uid, uid)) {
1151 check_dimming_rules_after_change (window);
1153 g_object_unref (G_OBJECT(header));
1157 g_object_unref(G_OBJECT(header));
1160 if (priv->row_reference) {
1161 gtk_tree_row_reference_free (priv->row_reference);
1164 /* Setup row_reference for the actual msg. */
1165 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1166 if (priv->row_reference == NULL) {
1167 g_warning("No reference for msg header item.");
1171 /* Now set up next_row_reference. */
1172 if (priv->next_row_reference) {
1173 gtk_tree_row_reference_free (priv->next_row_reference);
1176 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1177 select_next_valid_row (priv->header_model,
1178 &(priv->next_row_reference), FALSE, priv->is_outbox);
1180 /* Connect the remaining callbacks to become able to detect
1181 * changes in header-view. */
1182 priv->row_changed_handler =
1183 g_signal_connect (priv->header_model, "row-changed",
1184 G_CALLBACK (modest_msg_view_window_on_row_changed),
1186 priv->row_deleted_handler =
1187 g_signal_connect (priv->header_model, "row-deleted",
1188 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1190 priv->rows_reordered_handler =
1191 g_signal_connect (priv->header_model, "rows-reordered",
1192 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1195 check_dimming_rules_after_change (window);
1199 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1203 ModestMsgViewWindow *window)
1205 ModestMsgViewWindowPrivate *priv = NULL;
1206 gboolean already_changed = FALSE;
1208 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1210 /* If the current row was reordered select the proper next
1211 valid row. The same if the next row reference changes */
1212 if (priv->row_reference &&
1213 gtk_tree_row_reference_valid (priv->row_reference)) {
1215 path = gtk_tree_row_reference_get_path (priv->row_reference);
1216 if (gtk_tree_path_compare (path, arg1) == 0) {
1217 if (priv->next_row_reference) {
1218 gtk_tree_row_reference_free (priv->next_row_reference);
1220 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1221 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1222 already_changed = TRUE;
1224 gtk_tree_path_free (path);
1226 if (!already_changed &&
1227 priv->next_row_reference &&
1228 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1230 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1231 if (gtk_tree_path_compare (path, arg1) == 0) {
1232 if (priv->next_row_reference) {
1233 gtk_tree_row_reference_free (priv->next_row_reference);
1235 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1236 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1238 gtk_tree_path_free (path);
1240 check_dimming_rules_after_change (window);
1243 /* The modest_msg_view_window_update_model_replaced implements update
1244 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1245 * actually belongs to the header-view is the same as the TnyFolder of
1246 * the message of msg-view or not. If they are different, there is
1247 * nothing to do. If they are the same, then the model has replaced and
1248 * the reference in msg-view shall be replaced from the old model to
1249 * the new model. In this case the view will be detached from it's
1250 * header folder. From this point the next/prev buttons are dimmed.
1253 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1254 GtkTreeModel *model,
1255 const gchar *tny_folder_id)
1257 ModestMsgViewWindowPrivate *priv = NULL;
1258 ModestMsgViewWindow *window = NULL;
1260 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1261 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1263 window = MODEST_MSG_VIEW_WINDOW(observer);
1264 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1266 /* If there is an other folder in the header-view then we do
1267 * not care about it's model (msg list). Else if the
1268 * header-view shows the folder the msg shown by us is in, we
1269 * shall replace our model reference and make some check. */
1270 if(model == NULL || tny_folder_id == NULL ||
1271 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1274 /* Model is changed(replaced), so we should forget the old
1275 * one. Because there might be other references and there
1276 * might be some change on the model even if we unreferenced
1277 * it, we need to disconnect our signals here. */
1278 if (priv->header_model) {
1279 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1280 priv->row_changed_handler))
1281 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1282 priv->row_changed_handler);
1283 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1284 priv->row_deleted_handler))
1285 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1286 priv->row_deleted_handler);
1287 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1288 priv->row_inserted_handler))
1289 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1290 priv->row_inserted_handler);
1291 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1292 priv->rows_reordered_handler))
1293 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1294 priv->rows_reordered_handler);
1297 if (priv->row_reference)
1298 gtk_tree_row_reference_free (priv->row_reference);
1299 if (priv->next_row_reference)
1300 gtk_tree_row_reference_free (priv->next_row_reference);
1301 g_object_unref(priv->header_model);
1304 priv->row_changed_handler = 0;
1305 priv->row_deleted_handler = 0;
1306 priv->row_inserted_handler = 0;
1307 priv->rows_reordered_handler = 0;
1308 priv->next_row_reference = NULL;
1309 priv->row_reference = NULL;
1310 priv->header_model = NULL;
1313 priv->header_model = g_object_ref (model);
1315 /* Also we must connect to the new model for row insertions.
1316 * Only for insertions now. We will need other ones only after
1317 * the msg is show by msg-view is added to the new model. */
1318 priv->row_inserted_handler =
1319 g_signal_connect (priv->header_model, "row-inserted",
1320 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1323 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1324 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1328 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1330 ModestMsgViewWindowPrivate *priv= NULL;
1332 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1333 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1335 return priv->progress_hint;
1339 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1341 ModestMsgViewWindowPrivate *priv= NULL;
1343 TnyHeader *header = NULL;
1344 GtkTreePath *path = NULL;
1347 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1348 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1350 /* If the message was not obtained from a treemodel,
1351 * for instance if it was opened directly by the search UI:
1353 if (priv->header_model == NULL ||
1354 priv->row_reference == NULL ||
1355 !gtk_tree_row_reference_valid (priv->row_reference)) {
1356 msg = modest_msg_view_window_get_message (self);
1358 header = tny_msg_get_header (msg);
1359 g_object_unref (msg);
1364 /* Get iter of the currently selected message in the header view: */
1365 path = gtk_tree_row_reference_get_path (priv->row_reference);
1366 g_return_val_if_fail (path != NULL, NULL);
1367 gtk_tree_model_get_iter (priv->header_model,
1371 /* Get current message header */
1372 gtk_tree_model_get (priv->header_model, &iter,
1373 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1376 gtk_tree_path_free (path);
1381 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1383 ModestMsgViewWindowPrivate *priv;
1385 g_return_val_if_fail (self, NULL);
1387 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1389 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1393 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1395 ModestMsgViewWindowPrivate *priv;
1397 g_return_val_if_fail (self, NULL);
1399 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1401 return (const gchar*) priv->msg_uid;
1405 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1408 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1409 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1410 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1414 is_active = gtk_toggle_action_get_active (toggle);
1417 gtk_widget_show (priv->find_toolbar);
1418 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1420 gtk_widget_hide (priv->find_toolbar);
1421 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1424 /* update the toggle buttons status */
1425 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1427 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1432 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1433 ModestMsgViewWindow *obj)
1435 GtkToggleAction *toggle;
1436 ModestWindowPrivate *parent_priv;
1437 ModestMsgViewWindowPrivate *priv;
1439 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1440 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1442 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1443 gtk_toggle_action_set_active (toggle, FALSE);
1444 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1448 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1449 ModestMsgViewWindow *obj)
1451 gchar *current_search;
1452 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1454 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1455 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1459 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1461 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1462 g_free (current_search);
1463 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1467 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1469 g_free (priv->last_search);
1470 priv->last_search = g_strdup (current_search);
1471 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1474 hildon_banner_show_information (NULL, NULL,
1475 _HL("ckct_ib_find_no_matches"));
1476 g_free (priv->last_search);
1477 priv->last_search = NULL;
1479 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1482 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1483 hildon_banner_show_information (NULL, NULL,
1484 _HL("ckct_ib_find_search_complete"));
1485 g_free (priv->last_search);
1486 priv->last_search = NULL;
1488 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1492 g_free (current_search);
1497 modest_msg_view_window_set_zoom (ModestWindow *window,
1500 ModestMsgViewWindowPrivate *priv;
1501 ModestWindowPrivate *parent_priv;
1503 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1505 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1506 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1507 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1512 modest_msg_view_window_get_zoom (ModestWindow *window)
1514 ModestMsgViewWindowPrivate *priv;
1516 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1518 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1519 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1523 modest_msg_view_window_zoom_plus (ModestWindow *window)
1526 ModestMsgViewWindowPrivate *priv;
1530 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1531 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1533 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1535 if (zoom_level >= 2.0) {
1536 hildon_banner_show_information (NULL, NULL,
1537 _CS("ckct_ib_max_zoom_level_reached"));
1539 } else if (zoom_level >= 1.5) {
1541 } else if (zoom_level >= 1.2) {
1543 } else if (zoom_level >= 1.0) {
1545 } else if (zoom_level >= 0.8) {
1547 } else if (zoom_level >= 0.5) {
1553 /* set zoom level */
1554 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1555 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1556 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1557 g_free (banner_text);
1558 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1564 modest_msg_view_window_zoom_minus (ModestWindow *window)
1567 ModestMsgViewWindowPrivate *priv;
1571 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1572 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1574 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1576 if (zoom_level <= 0.5) {
1577 hildon_banner_show_information (NULL, NULL,
1578 _CS("ckct_ib_min_zoom_level_reached"));
1580 } else if (zoom_level <= 0.8) {
1582 } else if (zoom_level <= 1.0) {
1584 } else if (zoom_level <= 1.2) {
1586 } else if (zoom_level <= 1.5) {
1588 } else if (zoom_level <= 2.0) {
1594 /* set zoom level */
1595 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1596 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1597 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1598 g_free (banner_text);
1599 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1606 modest_msg_view_window_key_event (GtkWidget *window,
1612 focus = gtk_window_get_focus (GTK_WINDOW (window));
1614 /* for the find toolbar case */
1615 if (focus && GTK_IS_ENTRY (focus)) {
1616 if (event->keyval == GDK_BackSpace) {
1618 copy = gdk_event_copy ((GdkEvent *) event);
1619 gtk_widget_event (focus, copy);
1620 gdk_event_free (copy);
1625 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1626 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1627 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1628 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1629 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1630 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1631 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1632 /* gboolean return_value; */
1634 if (event->type == GDK_KEY_PRESS) {
1635 GtkScrollType scroll_type;
1637 switch (event->keyval) {
1640 scroll_type = GTK_SCROLL_STEP_UP; break;
1643 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1645 case GDK_KP_Page_Up:
1646 scroll_type = GTK_SCROLL_PAGE_UP; break;
1648 case GDK_KP_Page_Down:
1649 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1652 scroll_type = GTK_SCROLL_START; break;
1655 scroll_type = GTK_SCROLL_END; break;
1656 default: scroll_type = GTK_SCROLL_NONE;
1659 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1660 /* scroll_type, FALSE, &return_value); */
1671 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1674 ModestMsgViewWindowPrivate *priv;
1675 GtkTreeIter tmp_iter;
1676 gboolean is_last_selected;
1678 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1679 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1681 /*if no model (so no rows at all), then virtually we are the last*/
1682 if (!priv->header_model || !priv->row_reference)
1685 if (!gtk_tree_row_reference_valid (priv->row_reference))
1688 path = gtk_tree_row_reference_get_path (priv->row_reference);
1692 is_last_selected = TRUE;
1693 while (is_last_selected) {
1695 gtk_tree_path_next (path);
1696 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1698 gtk_tree_model_get (priv->header_model, &tmp_iter,
1699 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1702 if (msg_is_visible (header, priv->is_outbox))
1703 is_last_selected = FALSE;
1704 g_object_unref(G_OBJECT(header));
1707 gtk_tree_path_free (path);
1708 return is_last_selected;
1712 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1714 ModestMsgViewWindowPrivate *priv;
1716 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1717 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1719 return priv->header_model != NULL;
1723 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1725 ModestMsgViewWindowPrivate *priv;
1727 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1728 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1730 return priv->is_search_result;
1734 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1736 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1738 if (!check_outbox) {
1741 ModestTnySendQueueStatus status;
1742 status = modest_tny_all_send_queues_get_msg_status (header);
1743 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1744 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1749 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1752 ModestMsgViewWindowPrivate *priv;
1753 gboolean is_first_selected;
1754 GtkTreeIter tmp_iter;
1756 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1757 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1759 /*if no model (so no rows at all), then virtually we are the first*/
1760 if (!priv->header_model || !priv->row_reference)
1763 if (!gtk_tree_row_reference_valid (priv->row_reference))
1766 path = gtk_tree_row_reference_get_path (priv->row_reference);
1770 is_first_selected = TRUE;
1771 while (is_first_selected) {
1773 if(!gtk_tree_path_prev (path))
1775 /* Here the 'if' is needless for logic, but let make sure
1776 * iter is valid for gtk_tree_model_get. */
1777 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1779 gtk_tree_model_get (priv->header_model, &tmp_iter,
1780 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1783 if (msg_is_visible (header, priv->is_outbox))
1784 is_first_selected = FALSE;
1785 g_object_unref(G_OBJECT(header));
1788 gtk_tree_path_free (path);
1789 return is_first_selected;
1794 GtkTreeRowReference *row_reference;
1798 message_reader_performer (gboolean canceled,
1800 GtkWindow *parent_window,
1801 TnyAccount *account,
1804 ModestMailOperation *mail_op = NULL;
1805 MsgReaderInfo *info;
1807 info = (MsgReaderInfo *) user_data;
1808 if (canceled || err) {
1809 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1813 /* Register the header - it'll be unregistered in the callback */
1814 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1816 /* New mail operation */
1817 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1818 modest_ui_actions_disk_operations_error_handler,
1821 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1822 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1823 g_object_unref (mail_op);
1825 /* Update dimming rules */
1826 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1827 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1830 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1831 g_object_unref (info->header);
1832 g_slice_free (MsgReaderInfo, info);
1837 * Reads the message whose summary item is @header. It takes care of
1838 * several things, among others:
1840 * If the message was not previously downloaded then ask the user
1841 * before downloading. If there is no connection launch the connection
1842 * dialog. Update toolbar dimming rules.
1844 * Returns: TRUE if the mail operation was started, otherwise if the
1845 * user do not want to download the message, or if the user do not
1846 * want to connect, then the operation is not issued
1849 message_reader (ModestMsgViewWindow *window,
1850 ModestMsgViewWindowPrivate *priv,
1852 GtkTreeRowReference *row_reference)
1854 ModestWindowMgr *mgr;
1855 TnyAccount *account;
1857 MsgReaderInfo *info;
1859 g_return_val_if_fail (row_reference != NULL, FALSE);
1861 mgr = modest_runtime_get_window_mgr ();
1862 /* Msg download completed */
1863 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1865 /* We set the header from model while we're loading */
1866 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1867 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1869 /* Ask the user if he wants to download the message if
1871 if (!tny_device_is_online (modest_runtime_get_device())) {
1872 GtkResponseType response;
1874 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1875 _("mcen_nc_get_msg"));
1876 if (response == GTK_RESPONSE_CANCEL) {
1877 update_window_title (window);
1881 folder = tny_header_get_folder (header);
1882 info = g_slice_new (MsgReaderInfo);
1883 info->header = g_object_ref (header);
1884 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1886 /* Offer the connection dialog if necessary */
1887 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1889 TNY_FOLDER_STORE (folder),
1890 message_reader_performer,
1892 g_object_unref (folder);
1897 folder = tny_header_get_folder (header);
1898 account = tny_folder_get_account (folder);
1899 info = g_slice_new (MsgReaderInfo);
1900 info->header = g_object_ref (header);
1901 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1903 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1904 g_object_unref (account);
1905 g_object_unref (folder);
1911 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1913 ModestMsgViewWindowPrivate *priv;
1914 GtkTreePath *path= NULL;
1915 GtkTreeIter tmp_iter;
1917 gboolean retval = TRUE;
1918 GtkTreeRowReference *row_reference = NULL;
1920 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1921 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1923 if (!priv->row_reference)
1926 /* Update the next row reference if it's not valid. This could
1927 happen if for example the header which it was pointing to,
1928 was deleted. The best place to do it is in the row-deleted
1929 handler but the tinymail model do not work like the glib
1930 tree models and reports the deletion when the row is still
1932 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1933 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1934 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1935 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1938 if (priv->next_row_reference)
1939 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1943 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1945 gtk_tree_model_get_iter (priv->header_model,
1948 gtk_tree_path_free (path);
1950 gtk_tree_model_get (priv->header_model, &tmp_iter,
1951 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1954 /* Read the message & show it */
1955 if (!message_reader (window, priv, header, row_reference)) {
1958 gtk_tree_row_reference_free (row_reference);
1961 g_object_unref (header);
1967 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1969 ModestMsgViewWindowPrivate *priv = NULL;
1971 gboolean finished = FALSE;
1972 gboolean retval = FALSE;
1974 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1975 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1977 /* Return inmediatly if there is no header model */
1978 if (!priv->header_model || !priv->row_reference)
1981 path = gtk_tree_row_reference_get_path (priv->row_reference);
1982 while (!finished && gtk_tree_path_prev (path)) {
1986 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1987 gtk_tree_model_get (priv->header_model, &iter,
1988 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1992 if (msg_is_visible (header, priv->is_outbox)) {
1993 GtkTreeRowReference *row_reference;
1994 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1995 /* Read the message & show it */
1996 retval = message_reader (window, priv, header, row_reference);
1997 gtk_tree_row_reference_free (row_reference);
2001 g_object_unref (header);
2005 gtk_tree_path_free (path);
2010 view_msg_cb (ModestMailOperation *mail_op,
2017 ModestMsgViewWindow *self = NULL;
2018 ModestMsgViewWindowPrivate *priv = NULL;
2019 GtkTreeRowReference *row_reference = NULL;
2021 /* Unregister the header (it was registered before creating the mail operation) */
2022 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2024 row_reference = (GtkTreeRowReference *) user_data;
2026 gtk_tree_row_reference_free (row_reference);
2027 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2029 /* Restore window title */
2030 update_window_title (self);
2031 g_object_unref (self);
2036 /* If there was any error */
2037 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2038 gtk_tree_row_reference_free (row_reference);
2039 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2041 /* Restore window title */
2042 update_window_title (self);
2043 g_object_unref (self);
2048 /* Get the window */
2049 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2050 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2051 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2053 /* Update the row reference */
2054 if (priv->row_reference != NULL) {
2055 gtk_tree_row_reference_free (priv->row_reference);
2056 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2057 if (priv->next_row_reference != NULL) {
2058 gtk_tree_row_reference_free (priv->next_row_reference);
2060 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2061 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2064 /* Mark header as read */
2065 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2066 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2068 /* Set new message */
2069 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2070 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2071 modest_msg_view_window_update_priority (self);
2072 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2073 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2076 /* Set the new message uid of the window */
2077 if (priv->msg_uid) {
2078 g_free (priv->msg_uid);
2079 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2082 /* Notify the observers */
2083 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2084 0, priv->header_model, priv->row_reference);
2087 g_object_unref (self);
2088 gtk_tree_row_reference_free (row_reference);
2092 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2094 ModestMsgViewWindowPrivate *priv;
2096 TnyFolderType folder_type;
2098 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2100 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2102 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2106 folder = tny_msg_get_folder (msg);
2108 folder_type = modest_tny_folder_guess_folder_type (folder);
2109 g_object_unref (folder);
2111 g_object_unref (msg);
2119 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2121 ModestMsgViewWindowPrivate *priv;
2122 TnyHeader *header = NULL;
2123 TnyHeaderFlags flags = 0;
2125 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2127 if (priv->header_model && priv->row_reference) {
2129 GtkTreePath *path = NULL;
2131 path = gtk_tree_row_reference_get_path (priv->row_reference);
2132 g_return_if_fail (path != NULL);
2133 gtk_tree_model_get_iter (priv->header_model,
2135 gtk_tree_row_reference_get_path (priv->row_reference));
2137 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2139 gtk_tree_path_free (path);
2142 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2144 header = tny_msg_get_header (msg);
2145 g_object_unref (msg);
2150 flags = tny_header_get_flags (header);
2151 g_object_unref(G_OBJECT(header));
2154 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2159 toolbar_resize (ModestMsgViewWindow *self)
2161 ModestMsgViewWindowPrivate *priv = NULL;
2162 ModestWindowPrivate *parent_priv = NULL;
2164 gint static_button_size;
2165 ModestWindowMgr *mgr;
2167 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2168 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2169 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2171 mgr = modest_runtime_get_window_mgr ();
2172 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2174 if (parent_priv->toolbar) {
2175 /* left size buttons */
2176 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2177 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2178 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2179 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2180 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2181 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2182 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2183 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2184 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2185 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2186 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2187 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2188 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2189 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2190 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2191 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2193 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2194 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2195 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2196 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2201 modest_msg_view_window_show_toolbar (ModestWindow *self,
2202 gboolean show_toolbar)
2204 ModestMsgViewWindowPrivate *priv = NULL;
2205 ModestWindowPrivate *parent_priv;
2207 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2208 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2210 /* Set optimized view status */
2211 priv->optimized_view = !show_toolbar;
2213 if (!parent_priv->toolbar) {
2214 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2216 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2217 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2219 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2220 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2221 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2224 hildon_window_add_toolbar (HILDON_WINDOW (self),
2225 GTK_TOOLBAR (parent_priv->toolbar));
2230 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2231 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2232 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2234 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2235 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2236 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2238 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2241 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2242 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2247 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2249 ModestMsgViewWindow *window)
2251 if (!GTK_WIDGET_VISIBLE (window))
2254 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2258 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2260 ModestMsgViewWindowPrivate *priv;
2262 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2263 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2265 return priv->progress_hint;
2269 observers_empty (ModestMsgViewWindow *self)
2272 ModestMsgViewWindowPrivate *priv;
2273 gboolean is_empty = TRUE;
2274 guint pending_ops = 0;
2276 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2277 tmp = priv->progress_widgets;
2279 /* Check all observers */
2280 while (tmp && is_empty) {
2281 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2282 is_empty = pending_ops == 0;
2284 tmp = g_slist_next(tmp);
2291 on_account_removed (TnyAccountStore *account_store,
2292 TnyAccount *account,
2295 /* Do nothing if it's a transport account, because we only
2296 show the messages of a store account */
2297 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2298 const gchar *parent_acc = NULL;
2299 const gchar *our_acc = NULL;
2301 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2302 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2304 /* Close this window if I'm showing a message of the removed account */
2305 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2306 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2311 on_mail_operation_started (ModestMailOperation *mail_op,
2314 ModestMsgViewWindow *self;
2315 ModestMailOperationTypeOperation op_type;
2317 ModestMsgViewWindowPrivate *priv;
2318 GObject *source = NULL;
2320 self = MODEST_MSG_VIEW_WINDOW (user_data);
2321 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2322 op_type = modest_mail_operation_get_type_operation (mail_op);
2323 tmp = priv->progress_widgets;
2324 source = modest_mail_operation_get_source(mail_op);
2325 if (G_OBJECT (self) == source) {
2326 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2327 set_toolbar_transfer_mode(self);
2329 modest_progress_object_add_operation (
2330 MODEST_PROGRESS_OBJECT (tmp->data),
2332 tmp = g_slist_next (tmp);
2336 g_object_unref (source);
2340 on_mail_operation_finished (ModestMailOperation *mail_op,
2343 ModestMsgViewWindow *self;
2344 ModestMailOperationTypeOperation op_type;
2346 ModestMsgViewWindowPrivate *priv;
2348 self = MODEST_MSG_VIEW_WINDOW (user_data);
2349 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2350 op_type = modest_mail_operation_get_type_operation (mail_op);
2351 tmp = priv->progress_widgets;
2353 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2355 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2357 tmp = g_slist_next (tmp);
2360 /* If no more operations are being observed, NORMAL mode is enabled again */
2361 if (observers_empty (self)) {
2362 set_progress_hint (self, FALSE);
2366 /* Update dimming rules. We have to do this right here
2367 and not in view_msg_cb because at that point the
2368 transfer mode is still enabled so the dimming rule
2369 won't let the user delete the message that has been
2370 readed for example */
2371 check_dimming_rules_after_change (self);
2376 on_queue_changed (ModestMailOperationQueue *queue,
2377 ModestMailOperation *mail_op,
2378 ModestMailOperationQueueNotification type,
2379 ModestMsgViewWindow *self)
2381 ModestMsgViewWindowPrivate *priv;
2383 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2385 /* If this operations was created by another window, do nothing */
2386 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2389 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2390 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2392 "operation-started",
2393 G_CALLBACK (on_mail_operation_started),
2395 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2397 "operation-finished",
2398 G_CALLBACK (on_mail_operation_finished),
2400 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2401 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2403 "operation-started");
2404 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2406 "operation-finished");
2411 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2413 ModestMsgViewWindowPrivate *priv;
2414 TnyList *selected_attachments = NULL;
2416 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2417 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2419 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2420 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2422 return selected_attachments;
2426 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2432 gchar *filepath = (gchar *) user_data;
2434 if (cancelled || err) {
2436 modest_platform_information_banner (NULL, NULL,
2437 _KR("cerm_device_memory_full"));
2442 /* make the file read-only */
2443 g_chmod(filepath, 0444);
2445 /* Activate the file */
2446 modest_platform_activate_file (filepath, tny_mime_part_get_content_type (mime_part));
2454 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2455 TnyMimePart *mime_part)
2457 ModestMsgViewWindowPrivate *priv;
2458 const gchar *msg_uid;
2459 gchar *attachment_uid = NULL;
2460 gint attachment_index = 0;
2461 TnyList *attachments;
2463 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2464 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2465 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2467 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2468 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2469 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2470 g_object_unref (attachments);
2472 if (msg_uid && attachment_index >= 0) {
2473 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2476 if (mime_part == NULL) {
2477 gboolean error = FALSE;
2478 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2479 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2481 } else if (tny_list_get_length (selected_attachments) > 1) {
2482 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2486 iter = tny_list_create_iterator (selected_attachments);
2487 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2488 g_object_unref (iter);
2490 if (selected_attachments)
2491 g_object_unref (selected_attachments);
2496 g_object_ref (mime_part);
2499 if (tny_mime_part_is_purged (mime_part))
2502 if (!modest_tny_mime_part_is_msg (mime_part)) {
2503 gchar *filepath = NULL;
2504 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2505 gboolean show_error_banner = FALSE;
2506 TnyFsStream *temp_stream = NULL;
2507 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2510 if (temp_stream != NULL) {
2511 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2512 on_decode_to_stream_async_handler,
2514 g_strdup (filepath));
2515 g_object_unref (temp_stream);
2516 /* NOTE: files in the temporary area will be automatically
2517 * cleaned after some time if they are no longer in use */
2520 const gchar *content_type;
2521 /* the file may already exist but it isn't writable,
2522 * let's try to open it anyway */
2523 content_type = tny_mime_part_get_content_type (mime_part);
2524 modest_platform_activate_file (filepath, content_type);
2526 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2527 show_error_banner = TRUE;
2532 if (show_error_banner)
2533 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2535 /* message attachment */
2536 TnyHeader *header = NULL;
2537 ModestWindowMgr *mgr;
2538 ModestWindow *msg_win = NULL;
2541 header = tny_msg_get_header (TNY_MSG (mime_part));
2542 mgr = modest_runtime_get_window_mgr ();
2543 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2546 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2547 * thus, we don't do anything */
2548 g_warning ("window for is already being created");
2550 /* it's not found, so create a new window for it */
2551 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2552 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2553 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2555 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2556 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2557 mailbox, attachment_uid);
2558 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2559 modest_window_get_zoom (MODEST_WINDOW (window)));
2560 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2561 gtk_widget_show_all (GTK_WIDGET (msg_win));
2563 gtk_widget_destroy (GTK_WIDGET (msg_win));
2569 g_free (attachment_uid);
2571 g_object_unref (mime_part);
2583 GnomeVFSResult result;
2586 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2587 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2588 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2589 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2592 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2596 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2597 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2598 g_free (pair->filename);
2599 g_object_unref (pair->part);
2600 g_slice_free (SaveMimePartPair, pair);
2602 g_list_free (info->pairs);
2605 g_slice_free (SaveMimePartInfo, info);
2610 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2612 if (info->pairs != NULL) {
2613 save_mime_part_to_file (info);
2615 /* This is a GDK lock because we are an idle callback and
2616 * hildon_banner_show_information is or does Gtk+ code */
2618 gdk_threads_enter (); /* CHECKED */
2619 save_mime_part_info_free (info, TRUE);
2620 if (info->result == GNOME_VFS_OK) {
2621 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2622 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2623 hildon_banner_show_information (NULL, NULL,
2624 _KR("cerm_device_memory_full"));
2626 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2628 gdk_threads_leave (); /* CHECKED */
2635 save_mime_part_to_file (SaveMimePartInfo *info)
2637 GnomeVFSHandle *handle;
2639 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2641 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2642 if (info->result == GNOME_VFS_OK) {
2643 GError *error = NULL;
2644 stream = tny_vfs_stream_new (handle);
2645 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2646 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2648 if ((error->domain == TNY_ERROR_DOMAIN) &&
2649 (error->code == TNY_IO_ERROR_WRITE) &&
2650 (errno == ENOSPC)) {
2651 info->result = GNOME_VFS_ERROR_NO_SPACE;
2653 info->result = GNOME_VFS_ERROR_IO;
2656 g_object_unref (G_OBJECT (stream));
2657 g_object_unref (pair->part);
2658 g_slice_free (SaveMimePartPair, pair);
2659 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2661 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2662 save_mime_part_info_free (info, FALSE);
2665 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2670 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2672 gboolean is_ok = TRUE;
2673 gint replaced_files = 0;
2674 const GList *files = info->pairs;
2677 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2678 SaveMimePartPair *pair = iter->data;
2679 if (modest_utils_file_exists (pair->filename)) {
2683 if (replaced_files) {
2684 GtkWidget *confirm_overwrite_dialog;
2685 const gchar *message = (replaced_files == 1) ?
2686 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2687 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2688 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2691 gtk_widget_destroy (confirm_overwrite_dialog);
2695 save_mime_part_info_free (info, TRUE);
2697 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2703 save_attachments_response (GtkDialog *dialog,
2707 TnyList *mime_parts;
2709 GList *files_to_save = NULL;
2710 gchar *current_folder;
2712 mime_parts = TNY_LIST (user_data);
2714 if (arg1 != GTK_RESPONSE_OK)
2717 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2718 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2719 if (current_folder && current_folder != '\0') {
2721 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2722 current_folder,&err);
2724 g_debug ("Error storing latest used folder: %s", err->message);
2728 g_free (current_folder);
2730 if (!modest_utils_folder_writable (chooser_uri)) {
2731 hildon_banner_show_information
2732 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2736 iter = tny_list_create_iterator (mime_parts);
2737 while (!tny_iterator_is_done (iter)) {
2738 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2740 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2741 !tny_mime_part_is_purged (mime_part) &&
2742 (tny_mime_part_get_filename (mime_part) != NULL)) {
2743 SaveMimePartPair *pair;
2745 pair = g_slice_new0 (SaveMimePartPair);
2747 if (tny_list_get_length (mime_parts) > 1) {
2749 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2750 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2753 pair->filename = g_strdup (chooser_uri);
2755 pair->part = mime_part;
2756 files_to_save = g_list_prepend (files_to_save, pair);
2758 tny_iterator_next (iter);
2760 g_object_unref (iter);
2762 g_free (chooser_uri);
2764 if (files_to_save != NULL) {
2765 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2766 info->pairs = files_to_save;
2767 info->result = TRUE;
2768 save_mime_parts_to_file_with_checks (info);
2772 /* Free and close the dialog */
2773 g_object_unref (mime_parts);
2774 gtk_widget_destroy (GTK_WIDGET (dialog));
2778 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2779 TnyList *mime_parts)
2781 ModestMsgViewWindowPrivate *priv;
2782 GtkWidget *save_dialog = NULL;
2783 gchar *conf_folder = NULL;
2784 gchar *filename = NULL;
2785 gchar *save_multiple_str = NULL;
2787 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2788 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2790 if (mime_parts == NULL) {
2791 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2792 * selection available */
2793 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2794 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2795 g_object_unref (mime_parts);
2798 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2800 g_object_unref (mime_parts);
2806 g_object_ref (mime_parts);
2809 /* prepare dialog */
2810 if (tny_list_get_length (mime_parts) == 1) {
2812 /* only one attachment selected */
2813 iter = tny_list_create_iterator (mime_parts);
2814 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2815 g_object_unref (iter);
2816 if (!modest_tny_mime_part_is_msg (mime_part) &&
2817 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2818 !tny_mime_part_is_purged (mime_part)) {
2819 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2821 /* TODO: show any error? */
2822 g_warning ("Tried to save a non-file attachment");
2823 g_object_unref (mime_parts);
2826 g_object_unref (mime_part);
2828 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2829 tny_list_get_length (mime_parts));
2832 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2833 GTK_FILE_CHOOSER_ACTION_SAVE);
2836 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2837 if (conf_folder && conf_folder[0] != '\0') {
2838 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2841 /* Set the default folder to images folder */
2842 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2843 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2844 g_free (docs_folder);
2846 g_free (conf_folder);
2850 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2855 /* if multiple, set multiple string */
2856 if (save_multiple_str) {
2857 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2858 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2861 /* We must run this asynchronously, because the hildon dialog
2862 performs a gtk_dialog_run by itself which leads to gdk
2864 g_signal_connect (save_dialog, "response",
2865 G_CALLBACK (save_attachments_response), mime_parts);
2867 gtk_widget_show_all (save_dialog);
2871 show_remove_attachment_information (gpointer userdata)
2873 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2874 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2876 /* We're outside the main lock */
2877 gdk_threads_enter ();
2879 if (priv->remove_attachment_banner != NULL) {
2880 gtk_widget_destroy (priv->remove_attachment_banner);
2881 g_object_unref (priv->remove_attachment_banner);
2884 priv->remove_attachment_banner = g_object_ref (
2885 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2887 gdk_threads_leave ();
2893 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2895 ModestMsgViewWindowPrivate *priv;
2896 TnyList *mime_parts = NULL;
2897 gchar *confirmation_message;
2903 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2904 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2906 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2907 * because we don't have selection
2909 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2911 /* Remove already purged messages from mime parts list */
2912 iter = tny_list_create_iterator (mime_parts);
2913 while (!tny_iterator_is_done (iter)) {
2914 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2915 tny_iterator_next (iter);
2916 if (tny_mime_part_is_purged (part)) {
2917 tny_list_remove (mime_parts, (GObject *) part);
2919 g_object_unref (part);
2921 g_object_unref (iter);
2923 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
2924 tny_list_get_length (mime_parts) == 0) {
2925 g_object_unref (mime_parts);
2929 n_attachments = tny_list_get_length (mime_parts);
2930 if (n_attachments == 1) {
2934 iter = tny_list_create_iterator (mime_parts);
2935 part = (TnyMimePart *) tny_iterator_get_current (iter);
2936 g_object_unref (iter);
2937 if (modest_tny_mime_part_is_msg (part)) {
2939 header = tny_msg_get_header (TNY_MSG (part));
2940 filename = tny_header_dup_subject (header);
2941 g_object_unref (header);
2942 if (filename == NULL)
2943 filename = g_strdup (_("mail_va_no_subject"));
2945 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2947 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2949 g_object_unref (part);
2951 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2952 "mcen_nc_purge_files_text",
2953 n_attachments), n_attachments);
2955 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2956 confirmation_message);
2957 g_free (confirmation_message);
2959 if (response != GTK_RESPONSE_OK) {
2960 g_object_unref (mime_parts);
2964 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2966 iter = tny_list_create_iterator (mime_parts);
2967 while (!tny_iterator_is_done (iter)) {
2970 part = (TnyMimePart *) tny_iterator_get_current (iter);
2971 tny_mime_part_set_purged (TNY_MIME_PART (part));
2972 g_object_unref (part);
2973 tny_iterator_next (iter);
2975 g_object_unref (iter);
2977 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2978 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2979 tny_msg_rewrite_cache (msg);
2980 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2981 g_object_unref (msg);
2983 g_object_unref (mime_parts);
2985 if (priv->purge_timeout > 0) {
2986 g_source_remove (priv->purge_timeout);
2987 priv->purge_timeout = 0;
2990 if (priv->remove_attachment_banner) {
2991 gtk_widget_destroy (priv->remove_attachment_banner);
2992 g_object_unref (priv->remove_attachment_banner);
2993 priv->remove_attachment_banner = NULL;
3001 update_window_title (ModestMsgViewWindow *window)
3003 ModestMsgViewWindowPrivate *priv;
3005 TnyHeader *header = NULL;
3006 gchar *subject = NULL;
3008 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3010 /* Note that if the window is closed while we're retrieving
3011 the message, this widget could de deleted */
3012 if (!priv->msg_view)
3015 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3018 header = tny_msg_get_header (msg);
3019 subject = tny_header_dup_subject (header);
3020 g_object_unref (header);
3021 g_object_unref (msg);
3024 if ((subject == NULL)||(subject[0] == '\0')) {
3026 subject = g_strdup (_("mail_va_no_subject"));
3029 gtk_window_set_title (GTK_WINDOW (window), subject);
3034 on_move_focus (GtkWidget *widget,
3035 GtkDirectionType direction,
3038 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3042 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3044 GnomeVFSResult result;
3045 GnomeVFSHandle *handle = NULL;
3046 GnomeVFSFileInfo *info = NULL;
3049 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3050 if (result != GNOME_VFS_OK) {
3055 info = gnome_vfs_file_info_new ();
3056 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3057 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3058 /* We put a "safe" default size for going to cache */
3059 *expected_size = (300*1024);
3061 *expected_size = info->size;
3063 gnome_vfs_file_info_unref (info);
3065 stream = tny_vfs_stream_new (handle);
3074 TnyStream *output_stream;
3075 GtkWidget *msg_view;
3079 on_fetch_image_idle_refresh_view (gpointer userdata)
3082 FetchImageData *fidata = (FetchImageData *) userdata;
3083 g_message ("REFRESH VIEW");
3085 gdk_threads_enter ();
3086 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3087 g_message ("QUEUING DRAW");
3088 gtk_widget_queue_draw (fidata->msg_view);
3090 gdk_threads_leave ();
3092 g_object_unref (fidata->msg_view);
3093 g_slice_free (FetchImageData, fidata);
3098 on_fetch_image_thread (gpointer userdata)
3100 FetchImageData *fidata = (FetchImageData *) userdata;
3101 TnyStreamCache *cache;
3102 TnyStream *cache_stream;
3104 cache = modest_runtime_get_images_cache ();
3106 tny_stream_cache_get_stream (cache,
3108 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3109 (gpointer) fidata->uri);
3110 g_free (fidata->cache_id);
3111 g_free (fidata->uri);
3113 if (cache_stream != NULL) {
3116 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3119 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3120 if (G_UNLIKELY (nb_read < 0)) {
3122 } else if (G_LIKELY (nb_read > 0)) {
3123 gssize nb_written = 0;
3125 while (G_UNLIKELY (nb_written < nb_read)) {
3128 gdk_threads_enter ();
3129 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3130 nb_read - nb_written);
3131 gdk_threads_leave ();
3132 if (G_UNLIKELY (len < 0))
3138 gdk_threads_enter ();
3139 tny_stream_close (cache_stream);
3140 g_object_unref (cache_stream);
3141 gdk_threads_leave ();
3144 gdk_threads_enter ();
3145 tny_stream_close (fidata->output_stream);
3146 g_object_unref (fidata->output_stream);
3147 gdk_threads_leave ();
3149 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3155 on_fetch_image (ModestMsgView *msgview,
3158 ModestMsgViewWindow *window)
3160 const gchar *current_account;
3161 ModestMsgViewWindowPrivate *priv;
3162 FetchImageData *fidata;
3164 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3166 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3168 fidata = g_slice_new0 (FetchImageData);
3169 fidata->msg_view = g_object_ref (msgview);
3170 fidata->uri = g_strdup (uri);
3171 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3172 fidata->output_stream = g_object_ref (stream);
3174 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3175 g_object_unref (fidata->output_stream);
3176 g_free (fidata->cache_id);
3177 g_free (fidata->uri);
3178 g_object_unref (fidata->msg_view);
3179 g_slice_free (FetchImageData, fidata);
3180 tny_stream_close (stream);
3188 setup_menu (ModestMsgViewWindow *self)
3190 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3192 /* Settings menu buttons */
3193 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3194 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3195 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3196 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3197 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3198 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3200 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3201 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3202 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3203 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3204 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3205 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3207 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3208 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3209 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3210 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3211 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3212 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3214 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3215 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3216 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3217 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3218 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3219 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3223 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3225 ModestMsgViewWindowPrivate *priv;
3226 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3227 GSList *recipients = NULL;
3229 gboolean contacts_to_add = FALSE;
3231 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3235 header = modest_msg_view_window_get_header (self);
3238 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3239 g_object_unref (header);
3241 recipients = modest_tny_msg_get_all_recipients_list (msg);
3242 g_object_unref (msg);
3245 if (recipients != NULL) {
3246 GtkWidget *picker_dialog;
3247 GtkWidget *selector;
3249 gchar *selected = NULL;
3251 selector = hildon_touch_selector_new_text ();
3252 g_object_ref (selector);
3254 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3255 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3256 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3257 (const gchar *) node->data);
3258 contacts_to_add = TRUE;
3262 if (contacts_to_add) {
3265 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3266 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3268 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3269 HILDON_TOUCH_SELECTOR (selector));
3271 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3273 if (picker_result == GTK_RESPONSE_OK) {
3274 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3276 gtk_widget_destroy (picker_dialog);
3279 modest_address_book_add_address (selected);
3284 g_object_unref (selector);
3289 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3293 _modest_msg_view_window_map_event (GtkWidget *widget,
3297 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3298 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3300 if (priv->progress_hint) {
3301 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), TRUE);