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 gtk_widget_show_all (GTK_WIDGET(main_vbox));
541 modest_msg_view_window_disconnect_signals (ModestWindow *self)
543 ModestMsgViewWindowPrivate *priv;
544 GtkWidget *header_view = NULL;
545 GtkWindow *parent_window = NULL;
547 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
549 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
550 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
551 priv->clipboard_change_handler))
552 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
553 priv->clipboard_change_handler);
555 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
556 priv->queue_change_handler))
557 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
558 priv->queue_change_handler);
560 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
561 priv->account_removed_handler))
562 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
563 priv->account_removed_handler);
565 if (priv->header_model) {
566 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
567 priv->row_changed_handler))
568 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
569 priv->row_changed_handler);
571 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
572 priv->row_deleted_handler))
573 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
574 priv->row_deleted_handler);
576 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
577 priv->row_inserted_handler))
578 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
579 priv->row_inserted_handler);
581 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
582 priv->rows_reordered_handler))
583 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
584 priv->rows_reordered_handler);
587 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
588 priv->sighandlers = NULL;
590 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
591 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
592 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
594 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
595 MODEST_HEADER_VIEW_OBSERVER(self));
601 modest_msg_view_window_finalize (GObject *obj)
603 ModestMsgViewWindowPrivate *priv;
605 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
607 /* Sanity check: shouldn't be needed, the window mgr should
608 call this function before */
609 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
611 if (priv->header_model != NULL) {
612 g_object_unref (priv->header_model);
613 priv->header_model = NULL;
616 if (priv->remove_attachment_banner) {
617 gtk_widget_destroy (priv->remove_attachment_banner);
618 g_object_unref (priv->remove_attachment_banner);
619 priv->remove_attachment_banner = NULL;
622 if (priv->purge_timeout > 0) {
623 g_source_remove (priv->purge_timeout);
624 priv->purge_timeout = 0;
627 if (priv->row_reference) {
628 gtk_tree_row_reference_free (priv->row_reference);
629 priv->row_reference = NULL;
632 if (priv->next_row_reference) {
633 gtk_tree_row_reference_free (priv->next_row_reference);
634 priv->next_row_reference = NULL;
638 g_free (priv->msg_uid);
639 priv->msg_uid = NULL;
642 G_OBJECT_CLASS(parent_class)->finalize (obj);
646 select_next_valid_row (GtkTreeModel *model,
647 GtkTreeRowReference **row_reference,
651 GtkTreeIter tmp_iter;
653 GtkTreePath *next = NULL;
654 gboolean retval = FALSE, finished;
656 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
658 path = gtk_tree_row_reference_get_path (*row_reference);
659 gtk_tree_model_get_iter (model, &tmp_iter, path);
660 gtk_tree_row_reference_free (*row_reference);
661 *row_reference = NULL;
665 TnyHeader *header = NULL;
667 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
668 gtk_tree_model_get (model, &tmp_iter,
669 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
673 if (msg_is_visible (header, is_outbox)) {
674 next = gtk_tree_model_get_path (model, &tmp_iter);
675 *row_reference = gtk_tree_row_reference_new (model, next);
676 gtk_tree_path_free (next);
680 g_object_unref (header);
683 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
684 next = gtk_tree_model_get_path (model, &tmp_iter);
686 /* Ensure that we are not selecting the same */
687 if (gtk_tree_path_compare (path, next) != 0) {
688 gtk_tree_model_get (model, &tmp_iter,
689 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
692 if (msg_is_visible (header, is_outbox)) {
693 *row_reference = gtk_tree_row_reference_new (model, next);
697 g_object_unref (header);
701 /* If we ended up in the same message
702 then there is no valid next
706 gtk_tree_path_free (next);
708 /* If there are no more messages and we don't
709 want to start again in the first one then
710 there is no valid next message */
716 gtk_tree_path_free (path);
721 /* TODO: This should be in _init(), with the parameters as properties. */
723 modest_msg_view_window_construct (ModestMsgViewWindow *self,
724 const gchar *modest_account_name,
725 const gchar *msg_uid)
728 ModestMsgViewWindowPrivate *priv = NULL;
729 ModestWindowPrivate *parent_priv = NULL;
730 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
731 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
733 obj = G_OBJECT (self);
734 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
735 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
737 priv->msg_uid = g_strdup (msg_uid);
740 parent_priv->menubar = NULL;
742 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
743 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
746 /* Add common dimming rules */
747 modest_dimming_rules_group_add_rules (toolbar_rules_group,
748 modest_msg_view_toolbar_dimming_entries,
749 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
750 MODEST_WINDOW (self));
751 modest_dimming_rules_group_add_rules (clipboard_rules_group,
752 modest_msg_view_clipboard_dimming_entries,
753 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
754 MODEST_WINDOW (self));
756 /* Insert dimming rules group for this window */
757 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
758 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
759 g_object_unref (toolbar_rules_group);
760 g_object_unref (clipboard_rules_group);
762 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
764 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);
765 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
766 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
767 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
768 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
769 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
770 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
771 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
772 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
773 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
774 G_CALLBACK (modest_ui_actions_on_details), obj);
775 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
776 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
777 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
778 G_CALLBACK (on_fetch_image), obj);
780 g_signal_connect (G_OBJECT (obj), "key-release-event",
781 G_CALLBACK (modest_msg_view_window_key_event),
784 g_signal_connect (G_OBJECT (obj), "key-press-event",
785 G_CALLBACK (modest_msg_view_window_key_event),
788 g_signal_connect (G_OBJECT (obj), "move-focus",
789 G_CALLBACK (on_move_focus), obj);
791 g_signal_connect (G_OBJECT (obj), "expose-event",
792 G_CALLBACK (_modest_msg_view_window_map_event),
795 /* Mail Operation Queue */
796 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
798 G_CALLBACK (on_queue_changed),
801 /* Account manager */
802 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
804 G_CALLBACK(on_account_removed),
807 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
809 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
810 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
811 priv->last_search = NULL;
813 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
815 /* Init the clipboard actions dim status */
816 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
818 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
823 /* FIXME: parameter checks */
825 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
826 const gchar *modest_account_name,
827 const gchar *msg_uid,
829 GtkTreeRowReference *row_reference)
831 ModestMsgViewWindow *window = NULL;
832 ModestMsgViewWindowPrivate *priv = NULL;
833 TnyFolder *header_folder = NULL;
834 ModestHeaderView *header_view = NULL;
835 ModestWindow *main_window = NULL;
836 ModestWindowMgr *mgr = NULL;
839 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
842 mgr = modest_runtime_get_window_mgr ();
843 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
844 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
846 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
848 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
850 /* Remember the message list's TreeModel so we can detect changes
851 * and change the list selection when necessary: */
853 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
855 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
856 MODEST_MAIN_WINDOW(main_window),
857 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
860 if (header_view != NULL){
861 header_folder = modest_header_view_get_folder(header_view);
862 /* This could happen if the header folder was
863 unseleted before opening this msg window (for
864 example if the user selects an account in the
865 folder view of the main window */
867 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
868 priv->header_folder_id = tny_folder_get_id(header_folder);
869 g_assert(priv->header_folder_id != NULL);
870 g_object_unref(header_folder);
874 /* Setup row references and connect signals */
875 priv->header_model = g_object_ref (model);
878 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
879 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
880 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
882 priv->row_reference = NULL;
883 priv->next_row_reference = NULL;
886 /* Connect signals */
887 priv->row_changed_handler =
888 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
889 G_CALLBACK(modest_msg_view_window_on_row_changed),
891 priv->row_deleted_handler =
892 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
893 G_CALLBACK(modest_msg_view_window_on_row_deleted),
895 priv->row_inserted_handler =
896 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
897 G_CALLBACK(modest_msg_view_window_on_row_inserted),
899 priv->rows_reordered_handler =
900 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
901 G_CALLBACK(modest_msg_view_window_on_row_reordered),
904 if (header_view != NULL){
905 modest_header_view_add_observer(header_view,
906 MODEST_HEADER_VIEW_OBSERVER(window));
909 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
910 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
912 /* gtk_widget_show_all (GTK_WIDGET (window)); */
913 modest_msg_view_window_update_priority (window);
914 /* Check dimming rules */
915 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
916 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
917 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
919 return MODEST_WINDOW(window);
923 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
924 const gchar *modest_account_name,
925 const gchar *msg_uid,
926 GtkTreeRowReference *row_reference)
928 ModestMsgViewWindow *window = NULL;
929 ModestMsgViewWindowPrivate *priv = NULL;
930 TnyFolder *header_folder = NULL;
931 ModestWindowMgr *mgr = NULL;
935 mgr = modest_runtime_get_window_mgr ();
936 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
937 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
939 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
941 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
943 /* Remember the message list's TreeModel so we can detect changes
944 * and change the list selection when necessary: */
946 if (header_view != NULL){
947 header_folder = modest_header_view_get_folder(header_view);
948 /* This could happen if the header folder was
949 unseleted before opening this msg window (for
950 example if the user selects an account in the
951 folder view of the main window */
953 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
954 priv->header_folder_id = tny_folder_get_id(header_folder);
955 g_assert(priv->header_folder_id != NULL);
956 g_object_unref(header_folder);
960 /* Setup row references and connect signals */
961 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
962 g_object_ref (priv->header_model);
965 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
966 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
967 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
969 priv->row_reference = NULL;
970 priv->next_row_reference = NULL;
973 /* Connect signals */
974 priv->row_changed_handler =
975 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
976 G_CALLBACK(modest_msg_view_window_on_row_changed),
978 priv->row_deleted_handler =
979 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
980 G_CALLBACK(modest_msg_view_window_on_row_deleted),
982 priv->row_inserted_handler =
983 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
984 G_CALLBACK(modest_msg_view_window_on_row_inserted),
986 priv->rows_reordered_handler =
987 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
988 G_CALLBACK(modest_msg_view_window_on_row_reordered),
991 if (header_view != NULL){
992 modest_header_view_add_observer(header_view,
993 MODEST_HEADER_VIEW_OBSERVER(window));
996 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
998 path = gtk_tree_row_reference_get_path (row_reference);
999 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1001 gtk_tree_model_get (priv->header_model, &iter,
1002 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1004 message_reader (window, priv, header, row_reference);
1006 gtk_tree_path_free (path);
1008 /* Check dimming rules */
1009 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1010 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1011 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1013 return MODEST_WINDOW(window);
1017 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1018 const gchar *modest_account_name,
1019 const gchar *msg_uid)
1021 ModestMsgViewWindow *window = NULL;
1022 ModestMsgViewWindowPrivate *priv = NULL;
1023 ModestWindowMgr *mgr = NULL;
1025 mgr = modest_runtime_get_window_mgr ();
1026 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1027 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1028 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1030 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1032 /* Remember that this is a search result,
1033 * so we can disable some UI appropriately: */
1034 priv->is_search_result = TRUE;
1036 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1038 update_window_title (window);
1039 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1040 modest_msg_view_window_update_priority (window);
1042 /* Check dimming rules */
1043 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1044 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1045 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1047 return MODEST_WINDOW(window);
1051 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1052 const gchar *modest_account_name,
1053 const gchar *msg_uid)
1055 GObject *obj = NULL;
1056 ModestMsgViewWindowPrivate *priv;
1057 ModestWindowMgr *mgr = NULL;
1059 g_return_val_if_fail (msg, NULL);
1060 mgr = modest_runtime_get_window_mgr ();
1061 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1062 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1063 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1064 modest_account_name, msg_uid);
1066 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1067 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1069 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1071 /* Check dimming rules */
1072 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1073 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1074 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1076 return MODEST_WINDOW(obj);
1080 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1083 ModestMsgViewWindow *window)
1085 check_dimming_rules_after_change (window);
1089 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1091 ModestMsgViewWindow *window)
1093 check_dimming_rules_after_change (window);
1095 /* The window could have dissapeared */
1098 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1100 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1101 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1105 /* On insertions we check if the folder still has the message we are
1106 * showing or do not. If do not, we do nothing. Which means we are still
1107 * not attached to any header folder and thus next/prev buttons are
1108 * still dimmed. Once the message that is shown by msg-view is found, the
1109 * new model of header-view will be attached and the references will be set.
1110 * On each further insertions dimming rules will be checked. However
1111 * this requires extra CPU time at least works.
1112 * (An message might be deleted from TnyFolder and thus will not be
1113 * inserted into the model again for example if it is removed by the
1114 * imap server and the header view is refreshed.)
1117 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1118 GtkTreePath *tree_path,
1119 GtkTreeIter *tree_iter,
1120 ModestMsgViewWindow *window)
1122 ModestMsgViewWindowPrivate *priv = NULL;
1123 TnyHeader *header = NULL;
1125 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1126 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1128 g_assert (model == priv->header_model);
1130 /* Check if the newly inserted message is the same we are actually
1131 * showing. IF not, we should remain detached from the header model
1132 * and thus prev and next toolbar buttons should remain dimmed. */
1133 gtk_tree_model_get (model, tree_iter,
1134 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1137 if (TNY_IS_HEADER (header)) {
1140 uid = modest_tny_folder_get_header_unique_id (header);
1141 if (!g_str_equal(priv->msg_uid, uid)) {
1142 check_dimming_rules_after_change (window);
1144 g_object_unref (G_OBJECT(header));
1148 g_object_unref(G_OBJECT(header));
1151 if (priv->row_reference) {
1152 gtk_tree_row_reference_free (priv->row_reference);
1155 /* Setup row_reference for the actual msg. */
1156 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1157 if (priv->row_reference == NULL) {
1158 g_warning("No reference for msg header item.");
1162 /* Now set up next_row_reference. */
1163 if (priv->next_row_reference) {
1164 gtk_tree_row_reference_free (priv->next_row_reference);
1167 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1168 select_next_valid_row (priv->header_model,
1169 &(priv->next_row_reference), FALSE, priv->is_outbox);
1171 /* Connect the remaining callbacks to become able to detect
1172 * changes in header-view. */
1173 priv->row_changed_handler =
1174 g_signal_connect (priv->header_model, "row-changed",
1175 G_CALLBACK (modest_msg_view_window_on_row_changed),
1177 priv->row_deleted_handler =
1178 g_signal_connect (priv->header_model, "row-deleted",
1179 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1181 priv->rows_reordered_handler =
1182 g_signal_connect (priv->header_model, "rows-reordered",
1183 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1186 check_dimming_rules_after_change (window);
1190 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1194 ModestMsgViewWindow *window)
1196 ModestMsgViewWindowPrivate *priv = NULL;
1197 gboolean already_changed = FALSE;
1199 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1201 /* If the current row was reordered select the proper next
1202 valid row. The same if the next row reference changes */
1203 if (priv->row_reference &&
1204 gtk_tree_row_reference_valid (priv->row_reference)) {
1206 path = gtk_tree_row_reference_get_path (priv->row_reference);
1207 if (gtk_tree_path_compare (path, arg1) == 0) {
1208 if (priv->next_row_reference) {
1209 gtk_tree_row_reference_free (priv->next_row_reference);
1211 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1212 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1213 already_changed = TRUE;
1215 gtk_tree_path_free (path);
1217 if (!already_changed &&
1218 priv->next_row_reference &&
1219 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1221 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1222 if (gtk_tree_path_compare (path, arg1) == 0) {
1223 if (priv->next_row_reference) {
1224 gtk_tree_row_reference_free (priv->next_row_reference);
1226 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1227 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1229 gtk_tree_path_free (path);
1231 check_dimming_rules_after_change (window);
1234 /* The modest_msg_view_window_update_model_replaced implements update
1235 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1236 * actually belongs to the header-view is the same as the TnyFolder of
1237 * the message of msg-view or not. If they are different, there is
1238 * nothing to do. If they are the same, then the model has replaced and
1239 * the reference in msg-view shall be replaced from the old model to
1240 * the new model. In this case the view will be detached from it's
1241 * header folder. From this point the next/prev buttons are dimmed.
1244 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1245 GtkTreeModel *model,
1246 const gchar *tny_folder_id)
1248 ModestMsgViewWindowPrivate *priv = NULL;
1249 ModestMsgViewWindow *window = NULL;
1251 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1252 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1254 window = MODEST_MSG_VIEW_WINDOW(observer);
1255 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1257 /* If there is an other folder in the header-view then we do
1258 * not care about it's model (msg list). Else if the
1259 * header-view shows the folder the msg shown by us is in, we
1260 * shall replace our model reference and make some check. */
1261 if(model == NULL || tny_folder_id == NULL ||
1262 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1265 /* Model is changed(replaced), so we should forget the old
1266 * one. Because there might be other references and there
1267 * might be some change on the model even if we unreferenced
1268 * it, we need to disconnect our signals here. */
1269 if (priv->header_model) {
1270 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1271 priv->row_changed_handler))
1272 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1273 priv->row_changed_handler);
1274 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1275 priv->row_deleted_handler))
1276 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1277 priv->row_deleted_handler);
1278 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1279 priv->row_inserted_handler))
1280 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1281 priv->row_inserted_handler);
1282 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1283 priv->rows_reordered_handler))
1284 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1285 priv->rows_reordered_handler);
1288 if (priv->row_reference)
1289 gtk_tree_row_reference_free (priv->row_reference);
1290 if (priv->next_row_reference)
1291 gtk_tree_row_reference_free (priv->next_row_reference);
1292 g_object_unref(priv->header_model);
1295 priv->row_changed_handler = 0;
1296 priv->row_deleted_handler = 0;
1297 priv->row_inserted_handler = 0;
1298 priv->rows_reordered_handler = 0;
1299 priv->next_row_reference = NULL;
1300 priv->row_reference = NULL;
1301 priv->header_model = NULL;
1304 priv->header_model = g_object_ref (model);
1306 /* Also we must connect to the new model for row insertions.
1307 * Only for insertions now. We will need other ones only after
1308 * the msg is show by msg-view is added to the new model. */
1309 priv->row_inserted_handler =
1310 g_signal_connect (priv->header_model, "row-inserted",
1311 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1314 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1315 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1319 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1321 ModestMsgViewWindowPrivate *priv= NULL;
1323 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1324 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1326 return priv->progress_hint;
1330 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1332 ModestMsgViewWindowPrivate *priv= NULL;
1334 TnyHeader *header = NULL;
1335 GtkTreePath *path = NULL;
1338 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1339 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1341 /* If the message was not obtained from a treemodel,
1342 * for instance if it was opened directly by the search UI:
1344 if (priv->header_model == NULL ||
1345 priv->row_reference == NULL ||
1346 !gtk_tree_row_reference_valid (priv->row_reference)) {
1347 msg = modest_msg_view_window_get_message (self);
1349 header = tny_msg_get_header (msg);
1350 g_object_unref (msg);
1355 /* Get iter of the currently selected message in the header view: */
1356 path = gtk_tree_row_reference_get_path (priv->row_reference);
1357 g_return_val_if_fail (path != NULL, NULL);
1358 gtk_tree_model_get_iter (priv->header_model,
1362 /* Get current message header */
1363 gtk_tree_model_get (priv->header_model, &iter,
1364 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1367 gtk_tree_path_free (path);
1372 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1374 ModestMsgViewWindowPrivate *priv;
1376 g_return_val_if_fail (self, NULL);
1378 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1380 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1384 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1386 ModestMsgViewWindowPrivate *priv;
1388 g_return_val_if_fail (self, NULL);
1390 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1392 return (const gchar*) priv->msg_uid;
1396 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1399 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1400 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1401 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1405 is_active = gtk_toggle_action_get_active (toggle);
1408 gtk_widget_show (priv->find_toolbar);
1409 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1411 gtk_widget_hide (priv->find_toolbar);
1412 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1415 /* update the toggle buttons status */
1416 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1418 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1423 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1424 ModestMsgViewWindow *obj)
1426 GtkToggleAction *toggle;
1427 ModestWindowPrivate *parent_priv;
1428 ModestMsgViewWindowPrivate *priv;
1430 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1431 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1433 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1434 gtk_toggle_action_set_active (toggle, FALSE);
1435 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1439 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1440 ModestMsgViewWindow *obj)
1442 gchar *current_search;
1443 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1445 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1446 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1450 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1452 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1453 g_free (current_search);
1454 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1458 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1460 g_free (priv->last_search);
1461 priv->last_search = g_strdup (current_search);
1462 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1465 hildon_banner_show_information (NULL, NULL,
1466 _HL("ckct_ib_find_no_matches"));
1467 g_free (priv->last_search);
1468 priv->last_search = NULL;
1470 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1471 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1474 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1475 hildon_banner_show_information (NULL, NULL,
1476 _HL("ckct_ib_find_search_complete"));
1477 g_free (priv->last_search);
1478 priv->last_search = NULL;
1480 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1481 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1485 g_free (current_search);
1490 modest_msg_view_window_set_zoom (ModestWindow *window,
1493 ModestMsgViewWindowPrivate *priv;
1494 ModestWindowPrivate *parent_priv;
1496 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1498 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1499 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1500 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1505 modest_msg_view_window_get_zoom (ModestWindow *window)
1507 ModestMsgViewWindowPrivate *priv;
1509 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1511 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1512 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1516 modest_msg_view_window_zoom_plus (ModestWindow *window)
1519 ModestMsgViewWindowPrivate *priv;
1523 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1524 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1526 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1528 if (zoom_level >= 2.0) {
1529 hildon_banner_show_information (NULL, NULL,
1530 _CS("ckct_ib_max_zoom_level_reached"));
1532 } else if (zoom_level >= 1.5) {
1534 } else if (zoom_level >= 1.2) {
1536 } else if (zoom_level >= 1.0) {
1538 } else if (zoom_level >= 0.8) {
1540 } else if (zoom_level >= 0.5) {
1546 /* set zoom level */
1547 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1548 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1549 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1550 g_free (banner_text);
1551 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1557 modest_msg_view_window_zoom_minus (ModestWindow *window)
1560 ModestMsgViewWindowPrivate *priv;
1564 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1565 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1567 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1569 if (zoom_level <= 0.5) {
1570 hildon_banner_show_information (NULL, NULL,
1571 _CS("ckct_ib_min_zoom_level_reached"));
1573 } else if (zoom_level <= 0.8) {
1575 } else if (zoom_level <= 1.0) {
1577 } else if (zoom_level <= 1.2) {
1579 } else if (zoom_level <= 1.5) {
1581 } else if (zoom_level <= 2.0) {
1587 /* set zoom level */
1588 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1589 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1590 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1591 g_free (banner_text);
1592 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1599 modest_msg_view_window_key_event (GtkWidget *window,
1605 focus = gtk_window_get_focus (GTK_WINDOW (window));
1607 /* for the find toolbar case */
1608 if (focus && GTK_IS_ENTRY (focus)) {
1609 if (event->keyval == GDK_BackSpace) {
1611 copy = gdk_event_copy ((GdkEvent *) event);
1612 gtk_widget_event (focus, copy);
1613 gdk_event_free (copy);
1618 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1619 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1620 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1621 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1622 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1623 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1624 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1625 /* gboolean return_value; */
1627 if (event->type == GDK_KEY_PRESS) {
1628 GtkScrollType scroll_type;
1630 switch (event->keyval) {
1633 scroll_type = GTK_SCROLL_STEP_UP; break;
1636 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1638 case GDK_KP_Page_Up:
1639 scroll_type = GTK_SCROLL_PAGE_UP; break;
1641 case GDK_KP_Page_Down:
1642 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1645 scroll_type = GTK_SCROLL_START; break;
1648 scroll_type = GTK_SCROLL_END; break;
1649 default: scroll_type = GTK_SCROLL_NONE;
1652 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1653 /* scroll_type, FALSE, &return_value); */
1664 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1667 ModestMsgViewWindowPrivate *priv;
1668 GtkTreeIter tmp_iter;
1669 gboolean is_last_selected;
1671 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1672 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1674 /*if no model (so no rows at all), then virtually we are the last*/
1675 if (!priv->header_model || !priv->row_reference)
1678 if (!gtk_tree_row_reference_valid (priv->row_reference))
1681 path = gtk_tree_row_reference_get_path (priv->row_reference);
1685 is_last_selected = TRUE;
1686 while (is_last_selected) {
1688 gtk_tree_path_next (path);
1689 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1691 gtk_tree_model_get (priv->header_model, &tmp_iter,
1692 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1695 if (msg_is_visible (header, priv->is_outbox))
1696 is_last_selected = FALSE;
1697 g_object_unref(G_OBJECT(header));
1700 gtk_tree_path_free (path);
1701 return is_last_selected;
1705 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1707 ModestMsgViewWindowPrivate *priv;
1709 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1710 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1712 return priv->header_model != NULL;
1716 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1718 ModestMsgViewWindowPrivate *priv;
1720 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1721 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1723 return priv->is_search_result;
1727 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1729 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1731 if (!check_outbox) {
1734 ModestTnySendQueueStatus status;
1735 status = modest_tny_all_send_queues_get_msg_status (header);
1736 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1737 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1742 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1745 ModestMsgViewWindowPrivate *priv;
1746 gboolean is_first_selected;
1747 GtkTreeIter tmp_iter;
1749 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1750 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1752 /*if no model (so no rows at all), then virtually we are the first*/
1753 if (!priv->header_model || !priv->row_reference)
1756 if (!gtk_tree_row_reference_valid (priv->row_reference))
1759 path = gtk_tree_row_reference_get_path (priv->row_reference);
1763 is_first_selected = TRUE;
1764 while (is_first_selected) {
1766 if(!gtk_tree_path_prev (path))
1768 /* Here the 'if' is needless for logic, but let make sure
1769 * iter is valid for gtk_tree_model_get. */
1770 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1772 gtk_tree_model_get (priv->header_model, &tmp_iter,
1773 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1776 if (msg_is_visible (header, priv->is_outbox))
1777 is_first_selected = FALSE;
1778 g_object_unref(G_OBJECT(header));
1781 gtk_tree_path_free (path);
1782 return is_first_selected;
1787 GtkTreeRowReference *row_reference;
1791 message_reader_performer (gboolean canceled,
1793 GtkWindow *parent_window,
1794 TnyAccount *account,
1797 ModestMailOperation *mail_op = NULL;
1798 MsgReaderInfo *info;
1800 info = (MsgReaderInfo *) user_data;
1801 if (canceled || err) {
1805 /* Register the header - it'll be unregistered in the callback */
1806 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1808 /* New mail operation */
1809 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1810 modest_ui_actions_disk_operations_error_handler,
1813 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1814 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1815 g_object_unref (mail_op);
1817 /* Update dimming rules */
1818 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1819 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1822 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1823 g_object_unref (info->header);
1824 g_slice_free (MsgReaderInfo, info);
1829 * Reads the message whose summary item is @header. It takes care of
1830 * several things, among others:
1832 * If the message was not previously downloaded then ask the user
1833 * before downloading. If there is no connection launch the connection
1834 * dialog. Update toolbar dimming rules.
1836 * Returns: TRUE if the mail operation was started, otherwise if the
1837 * user do not want to download the message, or if the user do not
1838 * want to connect, then the operation is not issued
1841 message_reader (ModestMsgViewWindow *window,
1842 ModestMsgViewWindowPrivate *priv,
1844 GtkTreeRowReference *row_reference)
1846 ModestWindowMgr *mgr;
1847 TnyAccount *account;
1849 MsgReaderInfo *info;
1851 g_return_val_if_fail (row_reference != NULL, FALSE);
1853 mgr = modest_runtime_get_window_mgr ();
1854 /* Msg download completed */
1855 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1857 /* We set the header from model while we're loading */
1858 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1859 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1861 /* Ask the user if he wants to download the message if
1863 if (!tny_device_is_online (modest_runtime_get_device())) {
1864 GtkResponseType response;
1866 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1867 _("mcen_nc_get_msg"));
1868 if (response == GTK_RESPONSE_CANCEL)
1871 folder = tny_header_get_folder (header);
1872 info = g_slice_new (MsgReaderInfo);
1873 info->header = g_object_ref (header);
1874 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1876 /* Offer the connection dialog if necessary */
1877 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1879 TNY_FOLDER_STORE (folder),
1880 message_reader_performer,
1882 g_object_unref (folder);
1887 folder = tny_header_get_folder (header);
1888 account = tny_folder_get_account (folder);
1889 info = g_slice_new (MsgReaderInfo);
1890 info->header = g_object_ref (header);
1891 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1893 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1894 g_object_unref (account);
1895 g_object_unref (folder);
1901 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1903 ModestMsgViewWindowPrivate *priv;
1904 GtkTreePath *path= NULL;
1905 GtkTreeIter tmp_iter;
1907 gboolean retval = TRUE;
1908 GtkTreeRowReference *row_reference = NULL;
1910 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1911 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1913 if (!priv->row_reference)
1916 /* Update the next row reference if it's not valid. This could
1917 happen if for example the header which it was pointing to,
1918 was deleted. The best place to do it is in the row-deleted
1919 handler but the tinymail model do not work like the glib
1920 tree models and reports the deletion when the row is still
1922 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1923 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1924 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1925 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1928 if (priv->next_row_reference)
1929 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1933 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1935 gtk_tree_model_get_iter (priv->header_model,
1938 gtk_tree_path_free (path);
1940 gtk_tree_model_get (priv->header_model, &tmp_iter,
1941 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1944 /* Read the message & show it */
1945 if (!message_reader (window, priv, header, row_reference)) {
1948 gtk_tree_row_reference_free (row_reference);
1951 g_object_unref (header);
1957 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1959 ModestMsgViewWindowPrivate *priv = NULL;
1961 gboolean finished = FALSE;
1962 gboolean retval = FALSE;
1964 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1965 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1967 /* Return inmediatly if there is no header model */
1968 if (!priv->header_model || !priv->row_reference)
1971 path = gtk_tree_row_reference_get_path (priv->row_reference);
1972 while (!finished && gtk_tree_path_prev (path)) {
1976 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1977 gtk_tree_model_get (priv->header_model, &iter,
1978 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1982 if (msg_is_visible (header, priv->is_outbox)) {
1983 GtkTreeRowReference *row_reference;
1984 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1985 /* Read the message & show it */
1986 retval = message_reader (window, priv, header, row_reference);
1987 gtk_tree_row_reference_free (row_reference);
1991 g_object_unref (header);
1995 gtk_tree_path_free (path);
2000 view_msg_cb (ModestMailOperation *mail_op,
2007 ModestMsgViewWindow *self = NULL;
2008 ModestMsgViewWindowPrivate *priv = NULL;
2009 GtkTreeRowReference *row_reference = NULL;
2011 /* Unregister the header (it was registered before creating the mail operation) */
2012 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2014 row_reference = (GtkTreeRowReference *) user_data;
2016 gtk_tree_row_reference_free (row_reference);
2020 /* If there was any error */
2021 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2022 gtk_tree_row_reference_free (row_reference);
2026 /* Get the window */
2027 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2028 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2029 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2031 /* Update the row reference */
2032 if (priv->row_reference != NULL) {
2033 gtk_tree_row_reference_free (priv->row_reference);
2034 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2035 if (priv->next_row_reference != NULL) {
2036 gtk_tree_row_reference_free (priv->next_row_reference);
2038 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2039 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2042 /* Mark header as read */
2043 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2044 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2046 /* Set new message */
2047 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2048 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2049 modest_msg_view_window_update_priority (self);
2050 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2051 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2054 /* Set the new message uid of the window */
2055 if (priv->msg_uid) {
2056 g_free (priv->msg_uid);
2057 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2060 /* Notify the observers */
2061 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2062 0, priv->header_model, priv->row_reference);
2065 g_object_unref (self);
2066 gtk_tree_row_reference_free (row_reference);
2070 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2072 ModestMsgViewWindowPrivate *priv;
2074 TnyFolderType folder_type;
2076 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2078 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2080 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2084 folder = tny_msg_get_folder (msg);
2086 folder_type = modest_tny_folder_guess_folder_type (folder);
2087 g_object_unref (folder);
2089 g_object_unref (msg);
2097 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2099 ModestMsgViewWindowPrivate *priv;
2100 TnyHeader *header = NULL;
2101 TnyHeaderFlags flags = 0;
2103 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2105 if (priv->header_model && priv->row_reference) {
2107 GtkTreePath *path = NULL;
2109 path = gtk_tree_row_reference_get_path (priv->row_reference);
2110 g_return_if_fail (path != NULL);
2111 gtk_tree_model_get_iter (priv->header_model,
2113 gtk_tree_row_reference_get_path (priv->row_reference));
2115 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2117 gtk_tree_path_free (path);
2120 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2122 header = tny_msg_get_header (msg);
2123 g_object_unref (msg);
2128 flags = tny_header_get_flags (header);
2129 g_object_unref(G_OBJECT(header));
2132 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2137 toolbar_resize (ModestMsgViewWindow *self)
2139 ModestMsgViewWindowPrivate *priv = NULL;
2140 ModestWindowPrivate *parent_priv = NULL;
2142 gint static_button_size;
2143 ModestWindowMgr *mgr;
2145 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2146 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2147 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2149 mgr = modest_runtime_get_window_mgr ();
2150 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2152 if (parent_priv->toolbar) {
2153 /* left size buttons */
2154 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2155 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2156 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2157 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2158 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2159 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2160 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2161 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2162 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2163 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2164 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2165 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2166 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2167 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2168 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2169 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2171 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2172 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2173 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2174 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2180 modest_msg_view_window_show_toolbar (ModestWindow *self,
2181 gboolean show_toolbar)
2183 ModestMsgViewWindowPrivate *priv = NULL;
2184 ModestWindowPrivate *parent_priv;
2185 GtkWidget *reply_button = NULL, *menu = NULL;
2187 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2188 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2190 /* Set optimized view status */
2191 priv->optimized_view = !show_toolbar;
2193 if (!parent_priv->toolbar) {
2194 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2196 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2198 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2199 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2200 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2203 hildon_window_add_toolbar (HILDON_WINDOW (self),
2204 GTK_TOOLBAR (parent_priv->toolbar));
2206 /* Set reply button tap and hold menu */
2207 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2208 "/ToolBar/ToolbarMessageReply");
2209 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2210 "/ToolbarReplyCSM");
2211 if (menu && reply_button)
2212 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2216 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2217 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2218 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2220 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2221 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2222 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2224 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2227 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2228 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2233 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2235 ModestMsgViewWindow *window)
2237 if (!GTK_WIDGET_VISIBLE (window))
2240 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2244 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2246 ModestMsgViewWindowPrivate *priv;
2248 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2249 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2251 return priv->progress_hint;
2255 observers_empty (ModestMsgViewWindow *self)
2258 ModestMsgViewWindowPrivate *priv;
2259 gboolean is_empty = TRUE;
2260 guint pending_ops = 0;
2262 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2263 tmp = priv->progress_widgets;
2265 /* Check all observers */
2266 while (tmp && is_empty) {
2267 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2268 is_empty = pending_ops == 0;
2270 tmp = g_slist_next(tmp);
2277 on_account_removed (TnyAccountStore *account_store,
2278 TnyAccount *account,
2281 /* Do nothing if it's a transport account, because we only
2282 show the messages of a store account */
2283 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2284 const gchar *parent_acc = NULL;
2285 const gchar *our_acc = NULL;
2287 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2288 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2290 /* Close this window if I'm showing a message of the removed account */
2291 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2292 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2297 on_mail_operation_started (ModestMailOperation *mail_op,
2300 ModestMsgViewWindow *self;
2301 ModestMailOperationTypeOperation op_type;
2303 ModestMsgViewWindowPrivate *priv;
2304 GObject *source = NULL;
2306 self = MODEST_MSG_VIEW_WINDOW (user_data);
2307 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2308 op_type = modest_mail_operation_get_type_operation (mail_op);
2309 tmp = priv->progress_widgets;
2310 source = modest_mail_operation_get_source(mail_op);
2311 if (G_OBJECT (self) == source) {
2312 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2313 set_toolbar_transfer_mode(self);
2315 modest_progress_object_add_operation (
2316 MODEST_PROGRESS_OBJECT (tmp->data),
2318 tmp = g_slist_next (tmp);
2322 g_object_unref (source);
2326 on_mail_operation_finished (ModestMailOperation *mail_op,
2329 ModestMsgViewWindow *self;
2330 ModestMailOperationTypeOperation op_type;
2332 ModestMsgViewWindowPrivate *priv;
2334 self = MODEST_MSG_VIEW_WINDOW (user_data);
2335 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2336 op_type = modest_mail_operation_get_type_operation (mail_op);
2337 tmp = priv->progress_widgets;
2339 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2341 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2343 tmp = g_slist_next (tmp);
2346 /* If no more operations are being observed, NORMAL mode is enabled again */
2347 if (observers_empty (self)) {
2348 set_progress_hint (self, FALSE);
2351 /* Update dimming rules. We have to do this right here
2352 and not in view_msg_cb because at that point the
2353 transfer mode is still enabled so the dimming rule
2354 won't let the user delete the message that has been
2355 readed for example */
2356 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2357 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2362 on_queue_changed (ModestMailOperationQueue *queue,
2363 ModestMailOperation *mail_op,
2364 ModestMailOperationQueueNotification type,
2365 ModestMsgViewWindow *self)
2367 ModestMsgViewWindowPrivate *priv;
2369 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2371 /* If this operations was created by another window, do nothing */
2372 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2375 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2376 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2378 "operation-started",
2379 G_CALLBACK (on_mail_operation_started),
2381 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2383 "operation-finished",
2384 G_CALLBACK (on_mail_operation_finished),
2386 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2387 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2389 "operation-started");
2390 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2392 "operation-finished");
2397 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2399 ModestMsgViewWindowPrivate *priv;
2400 TnyList *selected_attachments = NULL;
2402 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2403 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2405 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2406 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2408 return selected_attachments;
2414 guint banner_idle_id;
2415 } DecodeAsyncHelper;
2418 decode_async_banner_idle (gpointer user_data)
2420 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2422 helper->banner_idle_id = 0;
2423 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2429 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2435 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2437 if (helper->banner_idle_id > 0) {
2438 g_source_remove (helper->banner_idle_id);
2439 helper->banner_idle_id = 0;
2441 if (helper->banner) {
2442 gtk_widget_destroy (helper->banner);
2443 helper->banner = NULL;
2445 if (cancelled || err) {
2446 modest_platform_information_banner (NULL, NULL,
2447 _("mail_ib_file_operation_failed"));
2451 /* make the file read-only */
2452 g_chmod(helper->filepath, 0444);
2454 /* Activate the file */
2455 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2459 g_free (helper->filepath);
2460 g_slice_free (DecodeAsyncHelper, helper);
2464 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2465 TnyMimePart *mime_part)
2467 ModestMsgViewWindowPrivate *priv;
2468 const gchar *msg_uid;
2469 gchar *attachment_uid = NULL;
2470 gint attachment_index = 0;
2471 TnyList *attachments;
2473 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2474 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2475 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2477 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2478 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2479 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2480 g_object_unref (attachments);
2482 if (msg_uid && attachment_index >= 0) {
2483 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2486 if (mime_part == NULL) {
2487 gboolean error = FALSE;
2488 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2489 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2491 } else if (tny_list_get_length (selected_attachments) > 1) {
2492 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2496 iter = tny_list_create_iterator (selected_attachments);
2497 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2498 g_object_unref (iter);
2500 g_object_unref (selected_attachments);
2505 g_object_ref (mime_part);
2508 if (tny_mime_part_is_purged (mime_part)) {
2509 g_object_unref (mime_part);
2513 if (!modest_tny_mime_part_is_msg (mime_part)) {
2514 gchar *filepath = NULL;
2515 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2516 gboolean show_error_banner = FALSE;
2517 TnyFsStream *temp_stream = NULL;
2518 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2521 if (temp_stream != NULL) {
2522 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2523 helper->filepath = g_strdup (filepath);
2524 helper->banner = NULL;
2525 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2526 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2527 on_decode_to_stream_async_handler,
2530 g_object_unref (temp_stream);
2531 /* NOTE: files in the temporary area will be automatically
2532 * cleaned after some time if they are no longer in use */
2535 const gchar *content_type;
2536 /* the file may already exist but it isn't writable,
2537 * let's try to open it anyway */
2538 content_type = tny_mime_part_get_content_type (mime_part);
2539 modest_platform_activate_file (filepath, content_type);
2541 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2542 show_error_banner = TRUE;
2547 if (show_error_banner)
2548 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2550 /* message attachment */
2551 TnyHeader *header = NULL;
2552 ModestWindowMgr *mgr;
2553 ModestWindow *msg_win = NULL;
2556 header = tny_msg_get_header (TNY_MSG (mime_part));
2557 mgr = modest_runtime_get_window_mgr ();
2558 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2561 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2562 * thus, we don't do anything */
2563 g_warning ("window for is already being created");
2565 /* it's not found, so create a new window for it */
2566 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2567 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2569 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2570 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2571 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2572 modest_window_get_zoom (MODEST_WINDOW (window)));
2573 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2574 gtk_widget_show_all (GTK_WIDGET (msg_win));
2577 g_object_unref (mime_part);
2590 GnomeVFSResult result;
2593 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2594 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2595 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2596 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2599 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2603 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2604 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2605 g_free (pair->filename);
2606 g_object_unref (pair->part);
2607 g_slice_free (SaveMimePartPair, pair);
2609 g_list_free (info->pairs);
2612 gtk_widget_destroy (info->banner);
2613 g_slice_free (SaveMimePartInfo, info);
2618 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2620 if (info->pairs != NULL) {
2621 save_mime_part_to_file (info);
2623 /* This is a GDK lock because we are an idle callback and
2624 * hildon_banner_show_information is or does Gtk+ code */
2626 gdk_threads_enter (); /* CHECKED */
2627 save_mime_part_info_free (info, TRUE);
2628 if (info->result == GNOME_VFS_OK) {
2629 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2630 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2631 hildon_banner_show_information (NULL, NULL,
2632 _KR("cerm_device_memory_full"));
2634 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2636 gdk_threads_leave (); /* CHECKED */
2643 save_mime_part_to_file (SaveMimePartInfo *info)
2645 GnomeVFSHandle *handle;
2647 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2649 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2650 if (info->result == GNOME_VFS_OK) {
2651 GError *error = NULL;
2652 stream = tny_vfs_stream_new (handle);
2653 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2654 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2656 if ((error->domain == TNY_ERROR_DOMAIN) &&
2657 (error->code = TNY_IO_ERROR_WRITE) &&
2658 (errno == ENOSPC)) {
2659 info->result = GNOME_VFS_ERROR_NO_SPACE;
2661 info->result = GNOME_VFS_ERROR_IO;
2664 g_object_unref (G_OBJECT (stream));
2665 g_object_unref (pair->part);
2666 g_slice_free (SaveMimePartPair, pair);
2667 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2669 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2670 save_mime_part_info_free (info, FALSE);
2673 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2678 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2680 gboolean is_ok = TRUE;
2681 gint replaced_files = 0;
2682 const GList *files = info->pairs;
2685 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2686 SaveMimePartPair *pair = iter->data;
2687 if (modest_utils_file_exists (pair->filename)) {
2691 if (replaced_files) {
2692 GtkWidget *confirm_overwrite_dialog;
2693 const gchar *message = (replaced_files == 1) ?
2694 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2695 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2696 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2699 gtk_widget_destroy (confirm_overwrite_dialog);
2703 save_mime_part_info_free (info, TRUE);
2705 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2706 _CS("sfil_ib_saving"));
2707 info->banner = banner;
2708 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2714 save_attachments_response (GtkDialog *dialog,
2718 TnyList *mime_parts;
2720 GList *files_to_save = NULL;
2722 mime_parts = TNY_LIST (user_data);
2724 if (arg1 != GTK_RESPONSE_OK)
2727 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2729 if (!modest_utils_folder_writable (chooser_uri)) {
2730 hildon_banner_show_information
2731 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2735 iter = tny_list_create_iterator (mime_parts);
2736 while (!tny_iterator_is_done (iter)) {
2737 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2739 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2740 !tny_mime_part_is_purged (mime_part) &&
2741 (tny_mime_part_get_filename (mime_part) != NULL)) {
2742 SaveMimePartPair *pair;
2744 pair = g_slice_new0 (SaveMimePartPair);
2746 if (tny_list_get_length (mime_parts) > 1) {
2748 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2749 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2752 pair->filename = g_strdup (chooser_uri);
2754 pair->part = mime_part;
2755 files_to_save = g_list_prepend (files_to_save, pair);
2757 tny_iterator_next (iter);
2759 g_object_unref (iter);
2761 g_free (chooser_uri);
2763 if (files_to_save != NULL) {
2764 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2765 info->pairs = files_to_save;
2766 info->result = TRUE;
2767 save_mime_parts_to_file_with_checks (info);
2771 /* Free and close the dialog */
2772 g_object_unref (mime_parts);
2773 gtk_widget_destroy (GTK_WIDGET (dialog));
2777 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2779 ModestMsgViewWindowPrivate *priv;
2780 GtkWidget *save_dialog = NULL;
2781 gchar *folder = NULL;
2782 gchar *filename = NULL;
2783 gchar *save_multiple_str = NULL;
2785 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2786 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2788 if (mime_parts == NULL) {
2789 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2790 * selection available */
2791 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2792 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2795 g_object_ref (mime_parts);
2798 /* prepare dialog */
2799 if (tny_list_get_length (mime_parts) == 1) {
2801 /* only one attachment selected */
2802 iter = tny_list_create_iterator (mime_parts);
2803 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2804 g_object_unref (iter);
2805 if (!modest_tny_mime_part_is_msg (mime_part) &&
2806 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2807 !tny_mime_part_is_purged (mime_part)) {
2808 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2810 /* TODO: show any error? */
2811 g_warning ("Tried to save a non-file attachment");
2812 g_object_unref (mime_parts);
2815 g_object_unref (mime_part);
2817 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2818 tny_list_get_length (mime_parts));
2821 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2822 GTK_FILE_CHOOSER_ACTION_SAVE);
2825 folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2826 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2831 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2836 /* if multiple, set multiple string */
2837 if (save_multiple_str) {
2838 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2839 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2842 /* We must run this asynchronously, because the hildon dialog
2843 performs a gtk_dialog_run by itself which leads to gdk
2845 g_signal_connect (save_dialog, "response",
2846 G_CALLBACK (save_attachments_response), mime_parts);
2848 gtk_widget_show_all (save_dialog);
2852 show_remove_attachment_information (gpointer userdata)
2854 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2855 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2857 /* We're outside the main lock */
2858 gdk_threads_enter ();
2860 if (priv->remove_attachment_banner != NULL) {
2861 gtk_widget_destroy (priv->remove_attachment_banner);
2862 g_object_unref (priv->remove_attachment_banner);
2865 priv->remove_attachment_banner = g_object_ref (
2866 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2868 gdk_threads_leave ();
2874 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2876 ModestMsgViewWindowPrivate *priv;
2877 TnyList *mime_parts = NULL;
2878 gchar *confirmation_message;
2884 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2885 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2887 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2888 * because we don't have selection
2890 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2892 /* Remove already purged messages from mime parts list */
2893 iter = tny_list_create_iterator (mime_parts);
2894 while (!tny_iterator_is_done (iter)) {
2895 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2896 tny_iterator_next (iter);
2897 if (tny_mime_part_is_purged (part)) {
2898 tny_list_remove (mime_parts, (GObject *) part);
2900 g_object_unref (part);
2902 g_object_unref (iter);
2904 if (tny_list_get_length (mime_parts) == 0) {
2905 g_object_unref (mime_parts);
2909 n_attachments = tny_list_get_length (mime_parts);
2910 if (n_attachments == 1) {
2914 iter = tny_list_create_iterator (mime_parts);
2915 part = (TnyMimePart *) tny_iterator_get_current (iter);
2916 g_object_unref (iter);
2917 if (modest_tny_mime_part_is_msg (part)) {
2919 header = tny_msg_get_header (TNY_MSG (part));
2920 filename = tny_header_dup_subject (header);
2921 g_object_unref (header);
2922 if (filename == NULL)
2923 filename = g_strdup (_("mail_va_no_subject"));
2925 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2927 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2929 g_object_unref (part);
2931 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2932 "mcen_nc_purge_files_text",
2933 n_attachments), n_attachments);
2935 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2936 confirmation_message);
2937 g_free (confirmation_message);
2939 if (response != GTK_RESPONSE_OK) {
2940 g_object_unref (mime_parts);
2944 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2946 iter = tny_list_create_iterator (mime_parts);
2947 while (!tny_iterator_is_done (iter)) {
2950 part = (TnyMimePart *) tny_iterator_get_current (iter);
2951 tny_mime_part_set_purged (TNY_MIME_PART (part));
2952 g_object_unref (part);
2953 tny_iterator_next (iter);
2955 g_object_unref (iter);
2957 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2958 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2959 tny_msg_rewrite_cache (msg);
2960 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2961 g_object_unref (msg);
2963 g_object_unref (mime_parts);
2965 if (priv->purge_timeout > 0) {
2966 g_source_remove (priv->purge_timeout);
2967 priv->purge_timeout = 0;
2970 if (priv->remove_attachment_banner) {
2971 gtk_widget_destroy (priv->remove_attachment_banner);
2972 g_object_unref (priv->remove_attachment_banner);
2973 priv->remove_attachment_banner = NULL;
2981 update_window_title (ModestMsgViewWindow *window)
2983 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2985 TnyHeader *header = NULL;
2986 gchar *subject = NULL;
2988 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2991 header = tny_msg_get_header (msg);
2992 subject = tny_header_dup_subject (header);
2993 g_object_unref (header);
2994 g_object_unref (msg);
2997 if ((subject == NULL)||(subject[0] == '\0')) {
2999 subject = g_strdup (_("mail_va_no_subject"));
3002 gtk_window_set_title (GTK_WINDOW (window), subject);
3006 static void on_move_focus (GtkWidget *widget,
3007 GtkDirectionType direction,
3010 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3014 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3016 GnomeVFSResult result;
3017 GnomeVFSHandle *handle = NULL;
3018 GnomeVFSFileInfo *info = NULL;
3021 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3022 if (result != GNOME_VFS_OK) {
3027 info = gnome_vfs_file_info_new ();
3028 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3029 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3030 /* We put a "safe" default size for going to cache */
3031 *expected_size = (300*1024);
3033 *expected_size = info->size;
3035 gnome_vfs_file_info_unref (info);
3037 stream = tny_vfs_stream_new (handle);
3046 TnyStream *output_stream;
3047 GtkWidget *msg_view;
3051 on_fetch_image_idle_refresh_view (gpointer userdata)
3054 FetchImageData *fidata = (FetchImageData *) userdata;
3055 g_message ("REFRESH VIEW");
3056 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3057 g_message ("QUEUING DRAW");
3058 gtk_widget_queue_draw (fidata->msg_view);
3060 g_object_unref (fidata->msg_view);
3061 g_slice_free (FetchImageData, fidata);
3066 on_fetch_image_thread (gpointer userdata)
3068 FetchImageData *fidata = (FetchImageData *) userdata;
3069 TnyStreamCache *cache;
3070 TnyStream *cache_stream;
3072 cache = modest_runtime_get_images_cache ();
3073 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3074 g_free (fidata->cache_id);
3075 g_free (fidata->uri);
3077 if (cache_stream != NULL) {
3078 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3079 tny_stream_close (cache_stream);
3080 g_object_unref (cache_stream);
3083 tny_stream_close (fidata->output_stream);
3084 g_object_unref (fidata->output_stream);
3087 gdk_threads_enter ();
3088 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3089 gdk_threads_leave ();
3095 on_fetch_image (ModestMsgView *msgview,
3098 ModestMsgViewWindow *window)
3100 const gchar *current_account;
3101 ModestMsgViewWindowPrivate *priv;
3102 FetchImageData *fidata;
3104 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3106 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3108 fidata = g_slice_new0 (FetchImageData);
3109 fidata->msg_view = g_object_ref (msgview);
3110 fidata->uri = g_strdup (uri);
3111 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3112 fidata->output_stream = g_object_ref (stream);
3114 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3115 g_object_unref (fidata->output_stream);
3116 g_free (fidata->cache_id);
3117 g_free (fidata->uri);
3118 g_object_unref (fidata->msg_view);
3119 g_slice_free (FetchImageData, fidata);
3120 tny_stream_close (stream);
3128 setup_menu (ModestMsgViewWindow *self)
3130 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3132 /* Settings menu buttons */
3133 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3134 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3135 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3136 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3137 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3138 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3140 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3141 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3142 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3143 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3144 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3145 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3147 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3148 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3149 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3150 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3151 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3152 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3154 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3155 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3156 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3157 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3158 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3159 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3163 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3165 ModestMsgViewWindowPrivate *priv;
3166 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3167 GSList *recipients = NULL;
3169 gboolean contacts_to_add = FALSE;
3171 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3175 header = modest_msg_view_window_get_header (self);
3178 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3179 g_object_unref (header);
3181 recipients = modest_tny_msg_get_all_recipients_list (msg);
3182 g_object_unref (msg);
3185 if (recipients != NULL) {
3186 GtkWidget *picker_dialog;
3187 GtkWidget *selector;
3189 gchar *selected = NULL;
3191 selector = hildon_touch_selector_new_text ();
3192 g_object_ref (selector);
3194 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3195 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3196 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3197 (const gchar *) node->data);
3198 contacts_to_add = TRUE;
3202 if (contacts_to_add) {
3205 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3206 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3208 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3209 HILDON_TOUCH_SELECTOR (selector));
3211 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3213 if (picker_result == GTK_RESPONSE_OK) {
3214 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3216 gtk_widget_destroy (picker_dialog);
3219 modest_address_book_add_address (selected);
3224 g_object_unref (selector);
3229 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3233 _modest_msg_view_window_map_event (GtkWidget *widget,
3237 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3238 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3240 if (priv->progress_hint) {
3241 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), TRUE);