1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-progress-object.h>
46 #include <modest-runtime.h>
47 #include <modest-window-priv.h>
48 #include <modest-tny-folder.h>
49 #include <modest-text-utils.h>
50 #include <modest-account-mgr-helpers.h>
51 #include <hildon/hildon-pannable-area.h>
52 #include <hildon/hildon-picker-dialog.h>
53 #include <hildon/hildon-app-menu.h>
54 #include "modest-defs.h"
55 #include "modest-hildon-includes.h"
56 #include "modest-ui-dimming-manager.h"
57 #include <gdk/gdkkeysyms.h>
58 #include <modest-tny-account.h>
59 #include <modest-mime-part-view.h>
60 #include <modest-isearch-view.h>
61 #include <modest-tny-mime-part.h>
62 #include <modest-address-book.h>
65 #include <glib/gstdio.h>
66 #include <modest-debug.h>
67 #include <modest-header-window.h>
69 #define MYDOCS_ENV "MYDOCSDIR"
70 #define DOCS_FOLDER ".documents"
72 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
73 struct _ModestMsgViewWindowPrivate {
76 GtkWidget *main_scroll;
77 GtkWidget *find_toolbar;
80 /* Progress observers */
81 GSList *progress_widgets;
84 GtkWidget *prev_toolitem;
85 GtkWidget *next_toolitem;
86 gboolean progress_hint;
88 /* Optimized view enabled */
89 gboolean optimized_view;
91 /* Whether this was created via the *_new_for_search_result() function. */
92 gboolean is_search_result;
94 /* Whether the message is in outbox */
97 /* A reference to the @model of the header view
98 * to allow selecting previous/next messages,
99 * if the message is currently selected in the header view.
101 const gchar *header_folder_id;
102 GtkTreeModel *header_model;
103 GtkTreeRowReference *row_reference;
104 GtkTreeRowReference *next_row_reference;
106 gulong clipboard_change_handler;
107 gulong queue_change_handler;
108 gulong account_removed_handler;
109 gulong row_changed_handler;
110 gulong row_deleted_handler;
111 gulong row_inserted_handler;
112 gulong rows_reordered_handler;
115 GtkWidget *remove_attachment_banner;
122 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
123 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
124 static void modest_header_view_observer_init(
125 ModestHeaderViewObserverIface *iface_class);
126 static void modest_msg_view_window_finalize (GObject *obj);
127 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
129 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
130 ModestMsgViewWindow *obj);
131 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
132 ModestMsgViewWindow *obj);
134 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
136 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
137 static void modest_msg_view_window_set_zoom (ModestWindow *window,
139 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
140 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
141 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
144 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
146 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
147 gboolean show_toolbar);
149 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
151 ModestMsgViewWindow *window);
153 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
156 ModestMsgViewWindow *window);
158 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
160 ModestMsgViewWindow *window);
162 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
163 GtkTreePath *tree_path,
164 GtkTreeIter *tree_iter,
165 ModestMsgViewWindow *window);
167 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
171 ModestMsgViewWindow *window);
173 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
175 const gchar *tny_folder_id);
177 static void on_queue_changed (ModestMailOperationQueue *queue,
178 ModestMailOperation *mail_op,
179 ModestMailOperationQueueNotification type,
180 ModestMsgViewWindow *self);
182 static void on_account_removed (TnyAccountStore *account_store,
186 static void on_move_focus (GtkWidget *widget,
187 GtkDirectionType direction,
190 static void view_msg_cb (ModestMailOperation *mail_op,
197 static void set_progress_hint (ModestMsgViewWindow *self,
200 static void update_window_title (ModestMsgViewWindow *window);
202 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
203 static void init_window (ModestMsgViewWindow *obj);
205 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
207 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
209 static gboolean on_fetch_image (ModestMsgView *msgview,
212 ModestMsgViewWindow *window);
214 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
215 GtkScrollType scroll_type,
218 static gboolean message_reader (ModestMsgViewWindow *window,
219 ModestMsgViewWindowPrivate *priv,
221 GtkTreeRowReference *row_reference);
223 static void setup_menu (ModestMsgViewWindow *self);
225 /* list my signals */
232 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
233 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
237 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
238 MODEST_TYPE_MSG_VIEW_WINDOW, \
239 ModestMsgViewWindowPrivate))
241 static GtkWindowClass *parent_class = NULL;
243 /* uncomment the following if you have defined any signals */
244 static guint signals[LAST_SIGNAL] = {0};
247 modest_msg_view_window_get_type (void)
249 static GType my_type = 0;
251 static const GTypeInfo my_info = {
252 sizeof(ModestMsgViewWindowClass),
253 NULL, /* base init */
254 NULL, /* base finalize */
255 (GClassInitFunc) modest_msg_view_window_class_init,
256 NULL, /* class finalize */
257 NULL, /* class data */
258 sizeof(ModestMsgViewWindow),
260 (GInstanceInitFunc) modest_msg_view_window_init,
263 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
264 "ModestMsgViewWindow",
267 static const GInterfaceInfo modest_header_view_observer_info =
269 (GInterfaceInitFunc) modest_header_view_observer_init,
270 NULL, /* interface_finalize */
271 NULL /* interface_data */
274 g_type_add_interface_static (my_type,
275 MODEST_TYPE_HEADER_VIEW_OBSERVER,
276 &modest_header_view_observer_info);
282 save_state (ModestWindow *self)
284 modest_widget_memory_save (modest_runtime_get_conf (),
286 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
290 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
291 GtkScrollType scroll_type,
295 ModestMsgViewWindowPrivate *priv;
296 gboolean return_value;
298 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
299 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
304 add_scroll_binding (GtkBindingSet *binding_set,
306 GtkScrollType scroll)
308 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
310 gtk_binding_entry_add_signal (binding_set, keyval, 0,
312 GTK_TYPE_SCROLL_TYPE, scroll,
313 G_TYPE_BOOLEAN, FALSE);
314 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
316 GTK_TYPE_SCROLL_TYPE, scroll,
317 G_TYPE_BOOLEAN, FALSE);
321 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
323 GObjectClass *gobject_class;
324 HildonWindowClass *hildon_window_class;
325 ModestWindowClass *modest_window_class;
326 GtkBindingSet *binding_set;
328 gobject_class = (GObjectClass*) klass;
329 hildon_window_class = (HildonWindowClass *) klass;
330 modest_window_class = (ModestWindowClass *) klass;
332 parent_class = g_type_class_peek_parent (klass);
333 gobject_class->finalize = modest_msg_view_window_finalize;
335 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
336 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
337 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
338 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
339 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
340 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
342 modest_window_class->save_state_func = save_state;
344 klass->scroll_child = modest_msg_view_window_scroll_child;
346 signals[MSG_CHANGED_SIGNAL] =
347 g_signal_new ("msg-changed",
348 G_TYPE_FROM_CLASS (gobject_class),
350 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
352 modest_marshal_VOID__POINTER_POINTER,
353 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
355 signals[SCROLL_CHILD_SIGNAL] =
356 g_signal_new ("scroll-child",
357 G_TYPE_FROM_CLASS (gobject_class),
358 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
359 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
361 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
362 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
364 binding_set = gtk_binding_set_by_class (klass);
365 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
366 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
367 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
368 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
369 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
370 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
372 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
376 static void modest_header_view_observer_init(
377 ModestHeaderViewObserverIface *iface_class)
379 iface_class->update_func = modest_msg_view_window_update_model_replaced;
383 modest_msg_view_window_init (ModestMsgViewWindow *obj)
385 ModestMsgViewWindowPrivate *priv;
386 ModestWindowPrivate *parent_priv = NULL;
387 GtkActionGroup *action_group = NULL;
388 GError *error = NULL;
389 GdkPixbuf *window_icon;
391 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
392 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
393 parent_priv->ui_manager = gtk_ui_manager_new();
395 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
396 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
398 /* Add common actions */
399 gtk_action_group_add_actions (action_group,
400 modest_action_entries,
401 G_N_ELEMENTS (modest_action_entries),
403 gtk_action_group_add_toggle_actions (action_group,
404 msg_view_toggle_action_entries,
405 G_N_ELEMENTS (msg_view_toggle_action_entries),
408 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
409 g_object_unref (action_group);
411 /* Load the UI definition */
412 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
415 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
416 g_error_free (error);
421 /* Add accelerators */
422 gtk_window_add_accel_group (GTK_WINDOW (obj),
423 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
425 priv->is_search_result = FALSE;
426 priv->is_outbox = FALSE;
428 priv->msg_view = NULL;
429 priv->header_model = NULL;
430 priv->header_folder_id = NULL;
431 priv->clipboard_change_handler = 0;
432 priv->queue_change_handler = 0;
433 priv->account_removed_handler = 0;
434 priv->row_changed_handler = 0;
435 priv->row_deleted_handler = 0;
436 priv->row_inserted_handler = 0;
437 priv->rows_reordered_handler = 0;
438 priv->progress_hint = FALSE;
440 priv->optimized_view = FALSE;
441 priv->purge_timeout = 0;
442 priv->remove_attachment_banner = NULL;
443 priv->msg_uid = NULL;
445 priv->sighandlers = NULL;
448 init_window (MODEST_MSG_VIEW_WINDOW(obj));
450 /* Set window icon */
451 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
453 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
454 g_object_unref (window_icon);
457 hildon_program_add_window (hildon_program_get_instance(),
464 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
466 ModestMsgViewWindowPrivate *priv = NULL;
468 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
470 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
472 set_progress_hint (self, TRUE);
478 set_progress_hint (ModestMsgViewWindow *self,
481 ModestWindowPrivate *parent_priv;
482 ModestMsgViewWindowPrivate *priv;
484 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
486 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
487 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
489 /* Sets current progress hint */
490 priv->progress_hint = enabled;
492 if (GTK_WIDGET_VISIBLE (self)) {
493 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
500 init_window (ModestMsgViewWindow *obj)
502 GtkWidget *main_vbox;
503 ModestMsgViewWindowPrivate *priv;
505 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
507 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
508 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
509 main_vbox = gtk_vbox_new (FALSE, 6);
510 #ifdef MODEST_TOOLKIT_HILDON2
511 priv->main_scroll = hildon_pannable_area_new ();
512 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
514 #ifdef MODEST_USE_MOZEMBED
515 priv->main_scroll = priv->msg_view;
516 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
518 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
519 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
521 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
522 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
523 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
526 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
527 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
529 priv->find_toolbar = hildon_find_toolbar_new (NULL);
530 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
531 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
533 gtk_widget_show_all (GTK_WIDGET(main_vbox));
537 modest_msg_view_window_disconnect_signals (ModestWindow *self)
539 ModestMsgViewWindowPrivate *priv;
540 GtkWidget *header_view = NULL;
541 GtkWindow *parent_window = NULL;
543 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
545 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
546 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
547 priv->clipboard_change_handler))
548 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
549 priv->clipboard_change_handler);
551 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
552 priv->queue_change_handler))
553 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
554 priv->queue_change_handler);
556 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
557 priv->account_removed_handler))
558 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
559 priv->account_removed_handler);
561 if (priv->header_model) {
562 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
563 priv->row_changed_handler))
564 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
565 priv->row_changed_handler);
567 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
568 priv->row_deleted_handler))
569 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
570 priv->row_deleted_handler);
572 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
573 priv->row_inserted_handler))
574 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
575 priv->row_inserted_handler);
577 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
578 priv->rows_reordered_handler))
579 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
580 priv->rows_reordered_handler);
583 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
584 priv->sighandlers = NULL;
586 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
587 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
588 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
590 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
591 MODEST_HEADER_VIEW_OBSERVER(self));
597 modest_msg_view_window_finalize (GObject *obj)
599 ModestMsgViewWindowPrivate *priv;
601 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
603 /* Sanity check: shouldn't be needed, the window mgr should
604 call this function before */
605 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
607 if (priv->header_model != NULL) {
608 g_object_unref (priv->header_model);
609 priv->header_model = NULL;
612 if (priv->remove_attachment_banner) {
613 gtk_widget_destroy (priv->remove_attachment_banner);
614 g_object_unref (priv->remove_attachment_banner);
615 priv->remove_attachment_banner = NULL;
618 if (priv->purge_timeout > 0) {
619 g_source_remove (priv->purge_timeout);
620 priv->purge_timeout = 0;
623 if (priv->row_reference) {
624 gtk_tree_row_reference_free (priv->row_reference);
625 priv->row_reference = NULL;
628 if (priv->next_row_reference) {
629 gtk_tree_row_reference_free (priv->next_row_reference);
630 priv->next_row_reference = NULL;
634 g_free (priv->msg_uid);
635 priv->msg_uid = NULL;
638 G_OBJECT_CLASS(parent_class)->finalize (obj);
642 select_next_valid_row (GtkTreeModel *model,
643 GtkTreeRowReference **row_reference,
647 GtkTreeIter tmp_iter;
649 GtkTreePath *next = NULL;
650 gboolean retval = FALSE, finished;
652 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
654 path = gtk_tree_row_reference_get_path (*row_reference);
655 gtk_tree_model_get_iter (model, &tmp_iter, path);
656 gtk_tree_row_reference_free (*row_reference);
657 *row_reference = NULL;
661 TnyHeader *header = NULL;
663 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
664 gtk_tree_model_get (model, &tmp_iter,
665 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
669 if (msg_is_visible (header, is_outbox)) {
670 next = gtk_tree_model_get_path (model, &tmp_iter);
671 *row_reference = gtk_tree_row_reference_new (model, next);
672 gtk_tree_path_free (next);
676 g_object_unref (header);
679 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
680 next = gtk_tree_model_get_path (model, &tmp_iter);
682 /* Ensure that we are not selecting the same */
683 if (gtk_tree_path_compare (path, next) != 0) {
684 gtk_tree_model_get (model, &tmp_iter,
685 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
688 if (msg_is_visible (header, is_outbox)) {
689 *row_reference = gtk_tree_row_reference_new (model, next);
693 g_object_unref (header);
697 /* If we ended up in the same message
698 then there is no valid next
702 gtk_tree_path_free (next);
704 /* If there are no more messages and we don't
705 want to start again in the first one then
706 there is no valid next message */
712 gtk_tree_path_free (path);
717 /* TODO: This should be in _init(), with the parameters as properties. */
719 modest_msg_view_window_construct (ModestMsgViewWindow *self,
720 const gchar *modest_account_name,
721 const gchar *msg_uid)
724 ModestMsgViewWindowPrivate *priv = NULL;
725 ModestWindowPrivate *parent_priv = NULL;
726 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
727 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
729 obj = G_OBJECT (self);
730 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
731 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
733 priv->msg_uid = g_strdup (msg_uid);
736 parent_priv->menubar = NULL;
737 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
739 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
740 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
743 /* Add common dimming rules */
744 modest_dimming_rules_group_add_rules (toolbar_rules_group,
745 modest_msg_view_toolbar_dimming_entries,
746 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
747 MODEST_WINDOW (self));
748 modest_dimming_rules_group_add_rules (clipboard_rules_group,
749 modest_msg_view_clipboard_dimming_entries,
750 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
751 MODEST_WINDOW (self));
753 /* Insert dimming rules group for this window */
754 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
755 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
756 g_object_unref (toolbar_rules_group);
757 g_object_unref (clipboard_rules_group);
759 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
761 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);
762 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
763 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
764 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
765 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
766 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
767 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
768 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
769 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
770 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
771 G_CALLBACK (modest_ui_actions_on_details), obj);
772 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
773 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
774 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
775 G_CALLBACK (on_fetch_image), obj);
777 g_signal_connect (G_OBJECT (obj), "key-release-event",
778 G_CALLBACK (modest_msg_view_window_key_event),
781 g_signal_connect (G_OBJECT (obj), "key-press-event",
782 G_CALLBACK (modest_msg_view_window_key_event),
785 g_signal_connect (G_OBJECT (obj), "move-focus",
786 G_CALLBACK (on_move_focus), obj);
788 /* Mail Operation Queue */
789 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
791 G_CALLBACK (on_queue_changed),
794 /* Account manager */
795 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
797 G_CALLBACK(on_account_removed),
800 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
802 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
803 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
804 priv->last_search = NULL;
806 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
808 /* Init the clipboard actions dim status */
809 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
811 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
816 /* FIXME: parameter checks */
818 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
819 const gchar *modest_account_name,
820 const gchar *msg_uid,
822 GtkTreeRowReference *row_reference)
824 ModestMsgViewWindow *window = NULL;
825 ModestMsgViewWindowPrivate *priv = NULL;
826 TnyFolder *header_folder = NULL;
827 ModestHeaderView *header_view = NULL;
828 ModestWindow *main_window = NULL;
829 ModestWindowMgr *mgr = NULL;
832 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
835 mgr = modest_runtime_get_window_mgr ();
836 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
837 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
839 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
841 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
843 /* Remember the message list's TreeModel so we can detect changes
844 * and change the list selection when necessary: */
846 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
848 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
849 MODEST_MAIN_WINDOW(main_window),
850 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
853 if (header_view != NULL){
854 header_folder = modest_header_view_get_folder(header_view);
855 /* This could happen if the header folder was
856 unseleted before opening this msg window (for
857 example if the user selects an account in the
858 folder view of the main window */
860 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
861 priv->header_folder_id = tny_folder_get_id(header_folder);
862 g_assert(priv->header_folder_id != NULL);
863 g_object_unref(header_folder);
867 /* Setup row references and connect signals */
868 priv->header_model = g_object_ref (model);
871 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
872 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
873 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
875 priv->row_reference = NULL;
876 priv->next_row_reference = NULL;
879 /* Connect signals */
880 priv->row_changed_handler =
881 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
882 G_CALLBACK(modest_msg_view_window_on_row_changed),
884 priv->row_deleted_handler =
885 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
886 G_CALLBACK(modest_msg_view_window_on_row_deleted),
888 priv->row_inserted_handler =
889 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
890 G_CALLBACK(modest_msg_view_window_on_row_inserted),
892 priv->rows_reordered_handler =
893 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
894 G_CALLBACK(modest_msg_view_window_on_row_reordered),
897 if (header_view != NULL){
898 modest_header_view_add_observer(header_view,
899 MODEST_HEADER_VIEW_OBSERVER(window));
902 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
903 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
905 /* gtk_widget_show_all (GTK_WIDGET (window)); */
906 modest_msg_view_window_update_priority (window);
907 /* Check dimming rules */
908 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
909 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
910 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
912 return MODEST_WINDOW(window);
916 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
917 const gchar *modest_account_name,
918 const gchar *msg_uid,
919 GtkTreeRowReference *row_reference)
921 ModestMsgViewWindow *window = NULL;
922 ModestMsgViewWindowPrivate *priv = NULL;
923 TnyFolder *header_folder = NULL;
924 ModestWindowMgr *mgr = NULL;
928 mgr = modest_runtime_get_window_mgr ();
929 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
930 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
932 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
934 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
936 /* Remember the message list's TreeModel so we can detect changes
937 * and change the list selection when necessary: */
939 if (header_view != NULL){
940 header_folder = modest_header_view_get_folder(header_view);
941 /* This could happen if the header folder was
942 unseleted before opening this msg window (for
943 example if the user selects an account in the
944 folder view of the main window */
946 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
947 priv->header_folder_id = tny_folder_get_id(header_folder);
948 g_assert(priv->header_folder_id != NULL);
949 g_object_unref(header_folder);
953 /* Setup row references and connect signals */
954 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
955 g_object_ref (priv->header_model);
958 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
959 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
960 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
962 priv->row_reference = NULL;
963 priv->next_row_reference = NULL;
966 /* Connect signals */
967 priv->row_changed_handler =
968 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
969 G_CALLBACK(modest_msg_view_window_on_row_changed),
971 priv->row_deleted_handler =
972 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
973 G_CALLBACK(modest_msg_view_window_on_row_deleted),
975 priv->row_inserted_handler =
976 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
977 G_CALLBACK(modest_msg_view_window_on_row_inserted),
979 priv->rows_reordered_handler =
980 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
981 G_CALLBACK(modest_msg_view_window_on_row_reordered),
984 if (header_view != NULL){
985 modest_header_view_add_observer(header_view,
986 MODEST_HEADER_VIEW_OBSERVER(window));
989 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
991 path = gtk_tree_row_reference_get_path (row_reference);
992 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
994 gtk_tree_model_get (priv->header_model, &iter,
995 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
997 message_reader (window, priv, header, row_reference);
999 gtk_tree_path_free (path);
1001 /* Check dimming rules */
1002 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1003 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1004 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1006 return MODEST_WINDOW(window);
1010 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1011 const gchar *modest_account_name,
1012 const gchar *msg_uid)
1014 ModestMsgViewWindow *window = NULL;
1015 ModestMsgViewWindowPrivate *priv = NULL;
1016 ModestWindowMgr *mgr = NULL;
1018 mgr = modest_runtime_get_window_mgr ();
1019 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1020 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1021 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1023 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1025 /* Remember that this is a search result,
1026 * so we can disable some UI appropriately: */
1027 priv->is_search_result = TRUE;
1029 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1031 update_window_title (window);
1032 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1033 modest_msg_view_window_update_priority (window);
1035 /* Check dimming rules */
1036 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1037 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1038 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1040 return MODEST_WINDOW(window);
1044 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1045 const gchar *modest_account_name,
1046 const gchar *msg_uid)
1048 GObject *obj = NULL;
1049 ModestMsgViewWindowPrivate *priv;
1050 ModestWindowMgr *mgr = NULL;
1052 g_return_val_if_fail (msg, NULL);
1053 mgr = modest_runtime_get_window_mgr ();
1054 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1055 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1056 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1057 modest_account_name, msg_uid);
1059 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1060 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1062 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1064 /* Check dimming rules */
1065 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1066 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1067 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1069 return MODEST_WINDOW(obj);
1073 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1076 ModestMsgViewWindow *window)
1078 check_dimming_rules_after_change (window);
1082 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1084 ModestMsgViewWindow *window)
1086 check_dimming_rules_after_change (window);
1088 /* The window could have dissapeared */
1091 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1093 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1094 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1098 /* On insertions we check if the folder still has the message we are
1099 * showing or do not. If do not, we do nothing. Which means we are still
1100 * not attached to any header folder and thus next/prev buttons are
1101 * still dimmed. Once the message that is shown by msg-view is found, the
1102 * new model of header-view will be attached and the references will be set.
1103 * On each further insertions dimming rules will be checked. However
1104 * this requires extra CPU time at least works.
1105 * (An message might be deleted from TnyFolder and thus will not be
1106 * inserted into the model again for example if it is removed by the
1107 * imap server and the header view is refreshed.)
1110 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1111 GtkTreePath *tree_path,
1112 GtkTreeIter *tree_iter,
1113 ModestMsgViewWindow *window)
1115 ModestMsgViewWindowPrivate *priv = NULL;
1116 TnyHeader *header = NULL;
1118 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1119 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1121 g_assert (model == priv->header_model);
1123 /* Check if the newly inserted message is the same we are actually
1124 * showing. IF not, we should remain detached from the header model
1125 * and thus prev and next toolbar buttons should remain dimmed. */
1126 gtk_tree_model_get (model, tree_iter,
1127 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1130 if (TNY_IS_HEADER (header)) {
1133 uid = modest_tny_folder_get_header_unique_id (header);
1134 if (!g_str_equal(priv->msg_uid, uid)) {
1135 check_dimming_rules_after_change (window);
1137 g_object_unref (G_OBJECT(header));
1141 g_object_unref(G_OBJECT(header));
1144 if (priv->row_reference) {
1145 gtk_tree_row_reference_free (priv->row_reference);
1148 /* Setup row_reference for the actual msg. */
1149 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1150 if (priv->row_reference == NULL) {
1151 g_warning("No reference for msg header item.");
1155 /* Now set up next_row_reference. */
1156 if (priv->next_row_reference) {
1157 gtk_tree_row_reference_free (priv->next_row_reference);
1160 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1161 select_next_valid_row (priv->header_model,
1162 &(priv->next_row_reference), FALSE, priv->is_outbox);
1164 /* Connect the remaining callbacks to become able to detect
1165 * changes in header-view. */
1166 priv->row_changed_handler =
1167 g_signal_connect (priv->header_model, "row-changed",
1168 G_CALLBACK (modest_msg_view_window_on_row_changed),
1170 priv->row_deleted_handler =
1171 g_signal_connect (priv->header_model, "row-deleted",
1172 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1174 priv->rows_reordered_handler =
1175 g_signal_connect (priv->header_model, "rows-reordered",
1176 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1179 check_dimming_rules_after_change (window);
1183 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1187 ModestMsgViewWindow *window)
1189 ModestMsgViewWindowPrivate *priv = NULL;
1190 gboolean already_changed = FALSE;
1192 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1194 /* If the current row was reordered select the proper next
1195 valid row. The same if the next row reference changes */
1196 if (priv->row_reference &&
1197 gtk_tree_row_reference_valid (priv->row_reference)) {
1199 path = gtk_tree_row_reference_get_path (priv->row_reference);
1200 if (gtk_tree_path_compare (path, arg1) == 0) {
1201 if (priv->next_row_reference) {
1202 gtk_tree_row_reference_free (priv->next_row_reference);
1204 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1205 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1206 already_changed = TRUE;
1208 gtk_tree_path_free (path);
1210 if (!already_changed &&
1211 priv->next_row_reference &&
1212 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1214 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1215 if (gtk_tree_path_compare (path, arg1) == 0) {
1216 if (priv->next_row_reference) {
1217 gtk_tree_row_reference_free (priv->next_row_reference);
1219 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1220 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1222 gtk_tree_path_free (path);
1224 check_dimming_rules_after_change (window);
1227 /* The modest_msg_view_window_update_model_replaced implements update
1228 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1229 * actually belongs to the header-view is the same as the TnyFolder of
1230 * the message of msg-view or not. If they are different, there is
1231 * nothing to do. If they are the same, then the model has replaced and
1232 * the reference in msg-view shall be replaced from the old model to
1233 * the new model. In this case the view will be detached from it's
1234 * header folder. From this point the next/prev buttons are dimmed.
1237 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1238 GtkTreeModel *model,
1239 const gchar *tny_folder_id)
1241 ModestMsgViewWindowPrivate *priv = NULL;
1242 ModestMsgViewWindow *window = NULL;
1244 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1245 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1247 window = MODEST_MSG_VIEW_WINDOW(observer);
1248 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1250 /* If there is an other folder in the header-view then we do
1251 * not care about it's model (msg list). Else if the
1252 * header-view shows the folder the msg shown by us is in, we
1253 * shall replace our model reference and make some check. */
1254 if(model == NULL || tny_folder_id == NULL ||
1255 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1258 /* Model is changed(replaced), so we should forget the old
1259 * one. Because there might be other references and there
1260 * might be some change on the model even if we unreferenced
1261 * it, we need to disconnect our signals here. */
1262 if (priv->header_model) {
1263 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1264 priv->row_changed_handler))
1265 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1266 priv->row_changed_handler);
1267 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1268 priv->row_deleted_handler))
1269 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1270 priv->row_deleted_handler);
1271 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1272 priv->row_inserted_handler))
1273 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1274 priv->row_inserted_handler);
1275 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1276 priv->rows_reordered_handler))
1277 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1278 priv->rows_reordered_handler);
1281 if (priv->row_reference)
1282 gtk_tree_row_reference_free (priv->row_reference);
1283 if (priv->next_row_reference)
1284 gtk_tree_row_reference_free (priv->next_row_reference);
1285 g_object_unref(priv->header_model);
1288 priv->row_changed_handler = 0;
1289 priv->row_deleted_handler = 0;
1290 priv->row_inserted_handler = 0;
1291 priv->rows_reordered_handler = 0;
1292 priv->next_row_reference = NULL;
1293 priv->row_reference = NULL;
1294 priv->header_model = NULL;
1297 priv->header_model = g_object_ref (model);
1299 /* Also we must connect to the new model for row insertions.
1300 * Only for insertions now. We will need other ones only after
1301 * the msg is show by msg-view is added to the new model. */
1302 priv->row_inserted_handler =
1303 g_signal_connect (priv->header_model, "row-inserted",
1304 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1307 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1308 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1312 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1314 ModestMsgViewWindowPrivate *priv= NULL;
1316 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1317 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1319 return priv->progress_hint;
1323 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1325 ModestMsgViewWindowPrivate *priv= NULL;
1327 TnyHeader *header = NULL;
1328 GtkTreePath *path = NULL;
1331 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1332 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1334 /* If the message was not obtained from a treemodel,
1335 * for instance if it was opened directly by the search UI:
1337 if (priv->header_model == NULL ||
1338 priv->row_reference == NULL ||
1339 !gtk_tree_row_reference_valid (priv->row_reference)) {
1340 msg = modest_msg_view_window_get_message (self);
1342 header = tny_msg_get_header (msg);
1343 g_object_unref (msg);
1348 /* Get iter of the currently selected message in the header view: */
1349 path = gtk_tree_row_reference_get_path (priv->row_reference);
1350 g_return_val_if_fail (path != NULL, NULL);
1351 gtk_tree_model_get_iter (priv->header_model,
1355 /* Get current message header */
1356 gtk_tree_model_get (priv->header_model, &iter,
1357 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1360 gtk_tree_path_free (path);
1365 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1367 ModestMsgViewWindowPrivate *priv;
1369 g_return_val_if_fail (self, NULL);
1371 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1373 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1377 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1379 ModestMsgViewWindowPrivate *priv;
1381 g_return_val_if_fail (self, NULL);
1383 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1385 return (const gchar*) priv->msg_uid;
1389 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1392 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1393 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1394 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1398 is_active = gtk_toggle_action_get_active (toggle);
1401 gtk_widget_show (priv->find_toolbar);
1402 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1404 gtk_widget_hide (priv->find_toolbar);
1405 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1408 /* update the toggle buttons status */
1409 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1411 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1416 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1417 ModestMsgViewWindow *obj)
1419 GtkToggleAction *toggle;
1420 ModestWindowPrivate *parent_priv;
1421 ModestMsgViewWindowPrivate *priv;
1423 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1424 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1426 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1427 gtk_toggle_action_set_active (toggle, FALSE);
1428 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1432 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1433 ModestMsgViewWindow *obj)
1435 gchar *current_search;
1436 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1438 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1439 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1443 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1445 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1446 g_free (current_search);
1447 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1451 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1453 g_free (priv->last_search);
1454 priv->last_search = g_strdup (current_search);
1455 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1458 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1459 g_free (priv->last_search);
1460 priv->last_search = NULL;
1462 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1463 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1466 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1467 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1468 g_free (priv->last_search);
1469 priv->last_search = NULL;
1471 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1472 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1476 g_free (current_search);
1481 modest_msg_view_window_set_zoom (ModestWindow *window,
1484 ModestMsgViewWindowPrivate *priv;
1485 ModestWindowPrivate *parent_priv;
1487 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1489 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1490 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1491 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1496 modest_msg_view_window_get_zoom (ModestWindow *window)
1498 ModestMsgViewWindowPrivate *priv;
1500 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1502 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1503 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1507 modest_msg_view_window_zoom_plus (ModestWindow *window)
1510 ModestMsgViewWindowPrivate *priv;
1512 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1513 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1515 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1517 if (zoom_level >= 2.0) {
1518 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1520 } else if (zoom_level >= 1.5) {
1522 } else if (zoom_level >= 1.2) {
1524 } else if (zoom_level >= 1.0) {
1526 } else if (zoom_level >= 0.8) {
1528 } else if (zoom_level >= 0.5) {
1534 /* set zoom level */
1535 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1542 modest_msg_view_window_zoom_minus (ModestWindow *window)
1545 ModestMsgViewWindowPrivate *priv;
1547 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1548 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1550 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1552 if (zoom_level <= 0.5) {
1553 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1555 } else if (zoom_level <= 0.8) {
1557 } else if (zoom_level <= 1.0) {
1559 } else if (zoom_level <= 1.2) {
1561 } else if (zoom_level <= 1.5) {
1563 } else if (zoom_level <= 2.0) {
1569 /* set zoom level */
1570 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1577 modest_msg_view_window_key_event (GtkWidget *window,
1583 focus = gtk_window_get_focus (GTK_WINDOW (window));
1585 /* for the find toolbar case */
1586 if (focus && GTK_IS_ENTRY (focus)) {
1587 if (event->keyval == GDK_BackSpace) {
1589 copy = gdk_event_copy ((GdkEvent *) event);
1590 gtk_widget_event (focus, copy);
1591 gdk_event_free (copy);
1596 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1597 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1598 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1599 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1600 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1601 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1602 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1603 /* gboolean return_value; */
1605 if (event->type == GDK_KEY_PRESS) {
1606 GtkScrollType scroll_type;
1608 switch (event->keyval) {
1611 scroll_type = GTK_SCROLL_STEP_UP; break;
1614 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1616 case GDK_KP_Page_Up:
1617 scroll_type = GTK_SCROLL_PAGE_UP; break;
1619 case GDK_KP_Page_Down:
1620 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1623 scroll_type = GTK_SCROLL_START; break;
1626 scroll_type = GTK_SCROLL_END; break;
1627 default: scroll_type = GTK_SCROLL_NONE;
1630 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1631 /* scroll_type, FALSE, &return_value); */
1642 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1645 ModestMsgViewWindowPrivate *priv;
1646 GtkTreeIter tmp_iter;
1647 gboolean is_last_selected;
1649 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1650 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1652 /*if no model (so no rows at all), then virtually we are the last*/
1653 if (!priv->header_model || !priv->row_reference)
1656 if (!gtk_tree_row_reference_valid (priv->row_reference))
1659 path = gtk_tree_row_reference_get_path (priv->row_reference);
1663 is_last_selected = TRUE;
1664 while (is_last_selected) {
1666 gtk_tree_path_next (path);
1667 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1669 gtk_tree_model_get (priv->header_model, &tmp_iter,
1670 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1673 if (msg_is_visible (header, priv->is_outbox))
1674 is_last_selected = FALSE;
1675 g_object_unref(G_OBJECT(header));
1678 gtk_tree_path_free (path);
1679 return is_last_selected;
1683 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1685 ModestMsgViewWindowPrivate *priv;
1687 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1688 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1690 return priv->header_model != NULL;
1694 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1696 ModestMsgViewWindowPrivate *priv;
1698 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1699 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1701 return priv->is_search_result;
1705 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1707 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1709 if (!check_outbox) {
1712 ModestTnySendQueueStatus status;
1713 status = modest_tny_all_send_queues_get_msg_status (header);
1714 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1715 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1720 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1723 ModestMsgViewWindowPrivate *priv;
1724 gboolean is_first_selected;
1725 GtkTreeIter tmp_iter;
1727 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1728 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1730 /*if no model (so no rows at all), then virtually we are the first*/
1731 if (!priv->header_model || !priv->row_reference)
1734 if (!gtk_tree_row_reference_valid (priv->row_reference))
1737 path = gtk_tree_row_reference_get_path (priv->row_reference);
1741 is_first_selected = TRUE;
1742 while (is_first_selected) {
1744 if(!gtk_tree_path_prev (path))
1746 /* Here the 'if' is needless for logic, but let make sure
1747 * iter is valid for gtk_tree_model_get. */
1748 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1750 gtk_tree_model_get (priv->header_model, &tmp_iter,
1751 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1754 if (msg_is_visible (header, priv->is_outbox))
1755 is_first_selected = FALSE;
1756 g_object_unref(G_OBJECT(header));
1759 gtk_tree_path_free (path);
1760 return is_first_selected;
1765 GtkTreeRowReference *row_reference;
1769 message_reader_performer (gboolean canceled,
1771 GtkWindow *parent_window,
1772 TnyAccount *account,
1775 ModestMailOperation *mail_op = NULL;
1776 MsgReaderInfo *info;
1778 info = (MsgReaderInfo *) user_data;
1779 if (canceled || err) {
1783 /* Register the header - it'll be unregistered in the callback */
1784 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1786 /* New mail operation */
1787 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1788 modest_ui_actions_disk_operations_error_handler,
1791 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1792 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1793 g_object_unref (mail_op);
1795 /* Update dimming rules */
1796 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1797 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1800 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1801 g_object_unref (info->header);
1802 g_slice_free (MsgReaderInfo, info);
1807 * Reads the message whose summary item is @header. It takes care of
1808 * several things, among others:
1810 * If the message was not previously downloaded then ask the user
1811 * before downloading. If there is no connection launch the connection
1812 * dialog. Update toolbar dimming rules.
1814 * Returns: TRUE if the mail operation was started, otherwise if the
1815 * user do not want to download the message, or if the user do not
1816 * want to connect, then the operation is not issued
1819 message_reader (ModestMsgViewWindow *window,
1820 ModestMsgViewWindowPrivate *priv,
1822 GtkTreeRowReference *row_reference)
1824 ModestWindowMgr *mgr;
1825 TnyAccount *account;
1827 MsgReaderInfo *info;
1829 g_return_val_if_fail (row_reference != NULL, FALSE);
1831 mgr = modest_runtime_get_window_mgr ();
1832 /* Msg download completed */
1833 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1834 /* Ask the user if he wants to download the message if
1836 if (!tny_device_is_online (modest_runtime_get_device())) {
1837 GtkResponseType response;
1839 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1840 _("mcen_nc_get_msg"));
1841 if (response == GTK_RESPONSE_CANCEL)
1844 folder = tny_header_get_folder (header);
1845 info = g_slice_new (MsgReaderInfo);
1846 info->header = g_object_ref (header);
1847 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1849 /* Offer the connection dialog if necessary */
1850 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1852 TNY_FOLDER_STORE (folder),
1853 message_reader_performer,
1855 g_object_unref (folder);
1860 folder = tny_header_get_folder (header);
1861 account = tny_folder_get_account (folder);
1862 info = g_slice_new (MsgReaderInfo);
1863 info->header = g_object_ref (header);
1864 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1866 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1867 g_object_unref (account);
1868 g_object_unref (folder);
1874 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1876 ModestMsgViewWindowPrivate *priv;
1877 GtkTreePath *path= NULL;
1878 GtkTreeIter tmp_iter;
1880 gboolean retval = TRUE;
1881 GtkTreeRowReference *row_reference = NULL;
1883 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1884 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1886 if (!priv->row_reference)
1889 /* Update the next row reference if it's not valid. This could
1890 happen if for example the header which it was pointing to,
1891 was deleted. The best place to do it is in the row-deleted
1892 handler but the tinymail model do not work like the glib
1893 tree models and reports the deletion when the row is still
1895 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1896 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1897 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1898 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1901 if (priv->next_row_reference)
1902 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1906 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1908 gtk_tree_model_get_iter (priv->header_model,
1911 gtk_tree_path_free (path);
1913 gtk_tree_model_get (priv->header_model, &tmp_iter,
1914 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1917 /* Read the message & show it */
1918 if (!message_reader (window, priv, header, row_reference)) {
1921 gtk_tree_row_reference_free (row_reference);
1924 g_object_unref (header);
1930 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1932 ModestMsgViewWindowPrivate *priv = NULL;
1934 gboolean finished = FALSE;
1935 gboolean retval = FALSE;
1937 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1938 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1940 /* Return inmediatly if there is no header model */
1941 if (!priv->header_model || !priv->row_reference)
1944 path = gtk_tree_row_reference_get_path (priv->row_reference);
1945 while (!finished && gtk_tree_path_prev (path)) {
1949 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1950 gtk_tree_model_get (priv->header_model, &iter,
1951 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1955 if (msg_is_visible (header, priv->is_outbox)) {
1956 GtkTreeRowReference *row_reference;
1957 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1958 /* Read the message & show it */
1959 retval = message_reader (window, priv, header, row_reference);
1960 gtk_tree_row_reference_free (row_reference);
1964 g_object_unref (header);
1968 gtk_tree_path_free (path);
1973 view_msg_cb (ModestMailOperation *mail_op,
1980 ModestMsgViewWindow *self = NULL;
1981 ModestMsgViewWindowPrivate *priv = NULL;
1982 GtkTreeRowReference *row_reference = NULL;
1984 /* Unregister the header (it was registered before creating the mail operation) */
1985 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1987 row_reference = (GtkTreeRowReference *) user_data;
1989 gtk_tree_row_reference_free (row_reference);
1993 /* If there was any error */
1994 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
1995 gtk_tree_row_reference_free (row_reference);
1999 /* Get the window */
2000 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2001 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2002 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2004 /* Update the row reference */
2005 if (priv->row_reference != NULL) {
2006 gtk_tree_row_reference_free (priv->row_reference);
2007 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2008 if (priv->next_row_reference != NULL) {
2009 gtk_tree_row_reference_free (priv->next_row_reference);
2011 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2012 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2015 /* Mark header as read */
2016 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2017 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2019 /* Set new message */
2020 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2021 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2022 modest_msg_view_window_update_priority (self);
2023 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2024 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2027 /* Set the new message uid of the window */
2028 if (priv->msg_uid) {
2029 g_free (priv->msg_uid);
2030 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2033 /* Notify the observers */
2034 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2035 0, priv->header_model, priv->row_reference);
2038 g_object_unref (self);
2039 gtk_tree_row_reference_free (row_reference);
2043 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2045 ModestMsgViewWindowPrivate *priv;
2047 TnyFolderType folder_type;
2049 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2051 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2053 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2057 folder = tny_msg_get_folder (msg);
2059 folder_type = modest_tny_folder_guess_folder_type (folder);
2060 g_object_unref (folder);
2062 g_object_unref (msg);
2070 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2072 ModestMsgViewWindowPrivate *priv;
2073 TnyHeader *header = NULL;
2074 TnyHeaderFlags flags = 0;
2076 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2078 if (priv->header_model && priv->row_reference) {
2080 GtkTreePath *path = NULL;
2082 path = gtk_tree_row_reference_get_path (priv->row_reference);
2083 g_return_if_fail (path != NULL);
2084 gtk_tree_model_get_iter (priv->header_model,
2086 gtk_tree_row_reference_get_path (priv->row_reference));
2088 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2090 gtk_tree_path_free (path);
2093 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2095 header = tny_msg_get_header (msg);
2096 g_object_unref (msg);
2101 flags = tny_header_get_flags (header);
2102 g_object_unref(G_OBJECT(header));
2105 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2110 toolbar_resize (ModestMsgViewWindow *self)
2112 ModestMsgViewWindowPrivate *priv = NULL;
2113 ModestWindowPrivate *parent_priv = NULL;
2115 gint static_button_size;
2116 ModestWindowMgr *mgr;
2118 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2119 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2120 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2122 mgr = modest_runtime_get_window_mgr ();
2123 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2125 if (parent_priv->toolbar) {
2126 /* left size buttons */
2127 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2128 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2129 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2130 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2131 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2132 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2133 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2134 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2135 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2136 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2137 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2138 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2139 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2140 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2141 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2142 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2144 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2145 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2146 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2147 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2153 modest_msg_view_window_show_toolbar (ModestWindow *self,
2154 gboolean show_toolbar)
2156 ModestMsgViewWindowPrivate *priv = NULL;
2157 ModestWindowPrivate *parent_priv;
2158 GtkWidget *reply_button = NULL, *menu = NULL;
2160 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2161 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2163 /* Set optimized view status */
2164 priv->optimized_view = !show_toolbar;
2166 if (!parent_priv->toolbar) {
2167 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2169 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2171 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2172 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2173 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2176 hildon_window_add_toolbar (HILDON_WINDOW (self),
2177 GTK_TOOLBAR (parent_priv->toolbar));
2179 /* Set reply button tap and hold menu */
2180 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2181 "/ToolBar/ToolbarMessageReply");
2182 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2183 "/ToolbarReplyCSM");
2184 if (menu && reply_button)
2185 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2189 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2190 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2191 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2193 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2194 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2195 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2197 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2200 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2201 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2206 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2208 ModestMsgViewWindow *window)
2210 if (!GTK_WIDGET_VISIBLE (window))
2213 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2217 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2219 ModestMsgViewWindowPrivate *priv;
2221 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2222 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2224 return priv->progress_hint;
2228 observers_empty (ModestMsgViewWindow *self)
2231 ModestMsgViewWindowPrivate *priv;
2232 gboolean is_empty = TRUE;
2233 guint pending_ops = 0;
2235 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2236 tmp = priv->progress_widgets;
2238 /* Check all observers */
2239 while (tmp && is_empty) {
2240 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2241 is_empty = pending_ops == 0;
2243 tmp = g_slist_next(tmp);
2250 on_account_removed (TnyAccountStore *account_store,
2251 TnyAccount *account,
2254 /* Do nothing if it's a transport account, because we only
2255 show the messages of a store account */
2256 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2257 const gchar *parent_acc = NULL;
2258 const gchar *our_acc = NULL;
2260 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2261 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2263 /* Close this window if I'm showing a message of the removed account */
2264 if (strcmp (parent_acc, our_acc) == 0)
2265 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2270 on_mail_operation_started (ModestMailOperation *mail_op,
2273 ModestMsgViewWindow *self;
2274 ModestMailOperationTypeOperation op_type;
2276 ModestMsgViewWindowPrivate *priv;
2277 GObject *source = NULL;
2279 self = MODEST_MSG_VIEW_WINDOW (user_data);
2280 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2281 op_type = modest_mail_operation_get_type_operation (mail_op);
2282 tmp = priv->progress_widgets;
2283 source = modest_mail_operation_get_source(mail_op);
2284 if (G_OBJECT (self) == source) {
2285 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2286 set_toolbar_transfer_mode(self);
2288 modest_progress_object_add_operation (
2289 MODEST_PROGRESS_OBJECT (tmp->data),
2291 tmp = g_slist_next (tmp);
2295 g_object_unref (source);
2299 on_mail_operation_finished (ModestMailOperation *mail_op,
2302 ModestMsgViewWindow *self;
2303 ModestMailOperationTypeOperation op_type;
2305 ModestMsgViewWindowPrivate *priv;
2307 self = MODEST_MSG_VIEW_WINDOW (user_data);
2308 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2309 op_type = modest_mail_operation_get_type_operation (mail_op);
2310 tmp = priv->progress_widgets;
2312 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2314 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2316 tmp = g_slist_next (tmp);
2319 /* If no more operations are being observed, NORMAL mode is enabled again */
2320 if (observers_empty (self)) {
2321 set_progress_hint (self, FALSE);
2324 /* Update dimming rules. We have to do this right here
2325 and not in view_msg_cb because at that point the
2326 transfer mode is still enabled so the dimming rule
2327 won't let the user delete the message that has been
2328 readed for example */
2329 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2330 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2335 on_queue_changed (ModestMailOperationQueue *queue,
2336 ModestMailOperation *mail_op,
2337 ModestMailOperationQueueNotification type,
2338 ModestMsgViewWindow *self)
2340 ModestMsgViewWindowPrivate *priv;
2342 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2344 /* If this operations was created by another window, do nothing */
2345 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2348 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2349 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2351 "operation-started",
2352 G_CALLBACK (on_mail_operation_started),
2354 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2356 "operation-finished",
2357 G_CALLBACK (on_mail_operation_finished),
2359 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2360 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2362 "operation-started");
2363 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2365 "operation-finished");
2370 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2372 ModestMsgViewWindowPrivate *priv;
2373 TnyList *selected_attachments = NULL;
2375 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2376 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2378 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2380 return selected_attachments;
2386 guint banner_idle_id;
2387 } DecodeAsyncHelper;
2390 decode_async_banner_idle (gpointer user_data)
2392 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2394 helper->banner_idle_id = 0;
2395 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2401 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2407 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2409 if (helper->banner_idle_id > 0) {
2410 g_source_remove (helper->banner_idle_id);
2411 helper->banner_idle_id = 0;
2413 if (helper->banner) {
2414 gtk_widget_destroy (helper->banner);
2415 helper->banner = NULL;
2417 if (cancelled || err) {
2418 modest_platform_information_banner (NULL, NULL,
2419 _("mail_ib_file_operation_failed"));
2423 /* make the file read-only */
2424 g_chmod(helper->filepath, 0444);
2426 /* Activate the file */
2427 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2431 g_free (helper->filepath);
2432 g_slice_free (DecodeAsyncHelper, helper);
2436 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2437 TnyMimePart *mime_part)
2439 ModestMsgViewWindowPrivate *priv;
2440 const gchar *msg_uid;
2441 gchar *attachment_uid = NULL;
2442 gint attachment_index = 0;
2443 TnyList *attachments;
2445 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2446 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2447 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2449 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2450 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2451 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2452 g_object_unref (attachments);
2454 if (msg_uid && attachment_index >= 0) {
2455 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2458 if (mime_part == NULL) {
2459 gboolean error = FALSE;
2460 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2461 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2463 } else if (tny_list_get_length (selected_attachments) > 1) {
2464 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2468 iter = tny_list_create_iterator (selected_attachments);
2469 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2470 g_object_unref (iter);
2472 g_object_unref (selected_attachments);
2477 g_object_ref (mime_part);
2480 if (tny_mime_part_is_purged (mime_part)) {
2481 g_object_unref (mime_part);
2485 if (!modest_tny_mime_part_is_msg (mime_part)) {
2486 gchar *filepath = NULL;
2487 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2488 gboolean show_error_banner = FALSE;
2489 TnyFsStream *temp_stream = NULL;
2490 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2493 if (temp_stream != NULL) {
2494 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2495 helper->filepath = g_strdup (filepath);
2496 helper->banner = NULL;
2497 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2498 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2499 on_decode_to_stream_async_handler,
2502 g_object_unref (temp_stream);
2503 /* NOTE: files in the temporary area will be automatically
2504 * cleaned after some time if they are no longer in use */
2507 const gchar *content_type;
2508 /* the file may already exist but it isn't writable,
2509 * let's try to open it anyway */
2510 content_type = tny_mime_part_get_content_type (mime_part);
2511 modest_platform_activate_file (filepath, content_type);
2513 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2514 show_error_banner = TRUE;
2519 if (show_error_banner)
2520 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2522 /* message attachment */
2523 TnyHeader *header = NULL;
2524 ModestWindowMgr *mgr;
2525 ModestWindow *msg_win = NULL;
2528 header = tny_msg_get_header (TNY_MSG (mime_part));
2529 mgr = modest_runtime_get_window_mgr ();
2530 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2533 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2534 * thus, we don't do anything */
2535 g_warning ("window for is already being created");
2537 /* it's not found, so create a new window for it */
2538 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2539 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2541 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2542 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2543 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2544 modest_window_get_zoom (MODEST_WINDOW (window)));
2545 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2546 gtk_widget_show_all (GTK_WIDGET (msg_win));
2549 g_object_unref (mime_part);
2562 GnomeVFSResult result;
2565 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2566 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2567 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2568 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2571 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2575 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2576 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2577 g_free (pair->filename);
2578 g_object_unref (pair->part);
2579 g_slice_free (SaveMimePartPair, pair);
2581 g_list_free (info->pairs);
2584 gtk_widget_destroy (info->banner);
2585 g_slice_free (SaveMimePartInfo, info);
2590 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2592 if (info->pairs != NULL) {
2593 save_mime_part_to_file (info);
2595 /* This is a GDK lock because we are an idle callback and
2596 * hildon_banner_show_information is or does Gtk+ code */
2598 gdk_threads_enter (); /* CHECKED */
2599 save_mime_part_info_free (info, TRUE);
2600 if (info->result == GNOME_VFS_OK) {
2601 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2602 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2603 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2604 "cerm_device_memory_full"));
2606 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2608 gdk_threads_leave (); /* CHECKED */
2615 save_mime_part_to_file (SaveMimePartInfo *info)
2617 GnomeVFSHandle *handle;
2619 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2621 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2622 if (info->result == GNOME_VFS_OK) {
2623 GError *error = NULL;
2624 stream = tny_vfs_stream_new (handle);
2625 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2626 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2628 info->result = GNOME_VFS_ERROR_IO;
2630 g_object_unref (G_OBJECT (stream));
2631 g_object_unref (pair->part);
2632 g_slice_free (SaveMimePartPair, pair);
2633 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2635 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2636 save_mime_part_info_free (info, FALSE);
2639 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2644 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2646 gboolean is_ok = TRUE;
2647 gint replaced_files = 0;
2648 const GList *files = info->pairs;
2651 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2652 SaveMimePartPair *pair = iter->data;
2653 if (modest_utils_file_exists (pair->filename)) {
2657 if (replaced_files) {
2658 GtkWidget *confirm_overwrite_dialog;
2659 const gchar *message = (replaced_files == 1) ?
2660 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2661 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2662 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2665 gtk_widget_destroy (confirm_overwrite_dialog);
2669 save_mime_part_info_free (info, TRUE);
2671 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2672 _CS("sfil_ib_saving"));
2673 info->banner = banner;
2674 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2680 save_attachments_response (GtkDialog *dialog,
2684 TnyList *mime_parts;
2686 GList *files_to_save = NULL;
2688 mime_parts = TNY_LIST (user_data);
2690 if (arg1 != GTK_RESPONSE_OK)
2693 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2695 if (!modest_utils_folder_writable (chooser_uri)) {
2696 hildon_banner_show_information
2697 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2701 iter = tny_list_create_iterator (mime_parts);
2702 while (!tny_iterator_is_done (iter)) {
2703 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2705 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2706 !tny_mime_part_is_purged (mime_part) &&
2707 (tny_mime_part_get_filename (mime_part) != NULL)) {
2708 SaveMimePartPair *pair;
2710 pair = g_slice_new0 (SaveMimePartPair);
2712 if (tny_list_get_length (mime_parts) > 1) {
2714 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2715 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2718 pair->filename = g_strdup (chooser_uri);
2720 pair->part = mime_part;
2721 files_to_save = g_list_prepend (files_to_save, pair);
2723 tny_iterator_next (iter);
2725 g_object_unref (iter);
2727 g_free (chooser_uri);
2729 if (files_to_save != NULL) {
2730 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2731 info->pairs = files_to_save;
2732 info->result = TRUE;
2733 save_mime_parts_to_file_with_checks (info);
2737 /* Free and close the dialog */
2738 g_object_unref (mime_parts);
2739 gtk_widget_destroy (GTK_WIDGET (dialog));
2743 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2745 ModestMsgViewWindowPrivate *priv;
2746 GtkWidget *save_dialog = NULL;
2747 gchar *folder = NULL;
2748 gchar *filename = NULL;
2749 gchar *save_multiple_str = NULL;
2751 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2752 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2754 if (mime_parts == NULL) {
2755 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2756 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2759 g_object_ref (mime_parts);
2762 /* prepare dialog */
2763 if (tny_list_get_length (mime_parts) == 1) {
2765 /* only one attachment selected */
2766 iter = tny_list_create_iterator (mime_parts);
2767 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2768 g_object_unref (iter);
2769 if (!modest_tny_mime_part_is_msg (mime_part) &&
2770 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2771 !tny_mime_part_is_purged (mime_part)) {
2772 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2774 /* TODO: show any error? */
2775 g_warning ("Tried to save a non-file attachment");
2776 g_object_unref (mime_parts);
2779 g_object_unref (mime_part);
2781 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2782 tny_list_get_length (mime_parts));
2785 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2786 GTK_FILE_CHOOSER_ACTION_SAVE);
2789 folder = g_build_filename (g_get_home_dir (), g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2790 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2795 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2800 /* if multiple, set multiple string */
2801 if (save_multiple_str) {
2802 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2803 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2806 /* We must run this asynchronously, because the hildon dialog
2807 performs a gtk_dialog_run by itself which leads to gdk
2809 g_signal_connect (save_dialog, "response",
2810 G_CALLBACK (save_attachments_response), mime_parts);
2812 gtk_widget_show_all (save_dialog);
2816 show_remove_attachment_information (gpointer userdata)
2818 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2819 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2821 /* We're outside the main lock */
2822 gdk_threads_enter ();
2824 if (priv->remove_attachment_banner != NULL) {
2825 gtk_widget_destroy (priv->remove_attachment_banner);
2826 g_object_unref (priv->remove_attachment_banner);
2829 priv->remove_attachment_banner = g_object_ref (
2830 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2832 gdk_threads_leave ();
2838 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2840 ModestMsgViewWindowPrivate *priv;
2841 TnyList *mime_parts = NULL;
2842 gchar *confirmation_message;
2848 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2849 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2852 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2854 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2856 /* Remove already purged messages from mime parts list */
2857 iter = tny_list_create_iterator (mime_parts);
2858 while (!tny_iterator_is_done (iter)) {
2859 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2860 tny_iterator_next (iter);
2861 if (tny_mime_part_is_purged (part)) {
2862 tny_list_remove (mime_parts, (GObject *) part);
2864 g_object_unref (part);
2866 g_object_unref (iter);
2868 if (tny_list_get_length (mime_parts) == 0) {
2869 g_object_unref (mime_parts);
2873 n_attachments = tny_list_get_length (mime_parts);
2874 if (n_attachments == 1) {
2878 iter = tny_list_create_iterator (mime_parts);
2879 part = (TnyMimePart *) tny_iterator_get_current (iter);
2880 g_object_unref (iter);
2881 if (modest_tny_mime_part_is_msg (part)) {
2883 header = tny_msg_get_header (TNY_MSG (part));
2884 filename = tny_header_dup_subject (header);
2885 g_object_unref (header);
2886 if (filename == NULL)
2887 filename = g_strdup (_("mail_va_no_subject"));
2889 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2891 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2893 g_object_unref (part);
2895 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2896 "mcen_nc_purge_files_text",
2897 n_attachments), n_attachments);
2899 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2900 confirmation_message);
2901 g_free (confirmation_message);
2903 if (response != GTK_RESPONSE_OK) {
2904 g_object_unref (mime_parts);
2908 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2910 iter = tny_list_create_iterator (mime_parts);
2911 while (!tny_iterator_is_done (iter)) {
2914 part = (TnyMimePart *) tny_iterator_get_current (iter);
2915 tny_mime_part_set_purged (TNY_MIME_PART (part));
2916 g_object_unref (part);
2917 tny_iterator_next (iter);
2919 g_object_unref (iter);
2921 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2922 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2923 tny_msg_rewrite_cache (msg);
2924 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2925 g_object_unref (msg);
2927 g_object_unref (mime_parts);
2929 if (priv->purge_timeout > 0) {
2930 g_source_remove (priv->purge_timeout);
2931 priv->purge_timeout = 0;
2934 if (priv->remove_attachment_banner) {
2935 gtk_widget_destroy (priv->remove_attachment_banner);
2936 g_object_unref (priv->remove_attachment_banner);
2937 priv->remove_attachment_banner = NULL;
2945 update_window_title (ModestMsgViewWindow *window)
2947 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2949 TnyHeader *header = NULL;
2950 gchar *subject = NULL;
2952 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2955 header = tny_msg_get_header (msg);
2956 subject = tny_header_dup_subject (header);
2957 g_object_unref (header);
2958 g_object_unref (msg);
2961 if ((subject == NULL)||(subject[0] == '\0')) {
2963 subject = g_strdup (_("mail_va_no_subject"));
2966 gtk_window_set_title (GTK_WINDOW (window), subject);
2970 static void on_move_focus (GtkWidget *widget,
2971 GtkDirectionType direction,
2974 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
2978 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
2980 GnomeVFSResult result;
2981 GnomeVFSHandle *handle = NULL;
2982 GnomeVFSFileInfo *info = NULL;
2985 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2986 if (result != GNOME_VFS_OK) {
2991 info = gnome_vfs_file_info_new ();
2992 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
2993 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
2994 /* We put a "safe" default size for going to cache */
2995 *expected_size = (300*1024);
2997 *expected_size = info->size;
2999 gnome_vfs_file_info_unref (info);
3001 stream = tny_vfs_stream_new (handle);
3010 TnyStream *output_stream;
3011 GtkWidget *msg_view;
3015 on_fetch_image_idle_refresh_view (gpointer userdata)
3018 FetchImageData *fidata = (FetchImageData *) userdata;
3019 g_message ("REFRESH VIEW");
3020 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3021 g_message ("QUEUING DRAW");
3022 gtk_widget_queue_draw (fidata->msg_view);
3024 g_object_unref (fidata->msg_view);
3025 g_slice_free (FetchImageData, fidata);
3030 on_fetch_image_thread (gpointer userdata)
3032 FetchImageData *fidata = (FetchImageData *) userdata;
3033 TnyStreamCache *cache;
3034 TnyStream *cache_stream;
3036 cache = modest_runtime_get_images_cache ();
3037 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3038 g_free (fidata->cache_id);
3039 g_free (fidata->uri);
3041 if (cache_stream != NULL) {
3042 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3043 tny_stream_close (cache_stream);
3044 g_object_unref (cache_stream);
3047 tny_stream_close (fidata->output_stream);
3048 g_object_unref (fidata->output_stream);
3051 gdk_threads_enter ();
3052 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3053 gdk_threads_leave ();
3059 on_fetch_image (ModestMsgView *msgview,
3062 ModestMsgViewWindow *window)
3064 const gchar *current_account;
3065 ModestMsgViewWindowPrivate *priv;
3066 FetchImageData *fidata;
3068 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3070 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3072 fidata = g_slice_new0 (FetchImageData);
3073 fidata->msg_view = g_object_ref (msgview);
3074 fidata->uri = g_strdup (uri);
3075 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3076 fidata->output_stream = g_object_ref (stream);
3078 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3079 g_object_unref (fidata->output_stream);
3080 g_free (fidata->cache_id);
3081 g_free (fidata->uri);
3082 g_object_unref (fidata->msg_view);
3083 g_slice_free (FetchImageData, fidata);
3084 tny_stream_close (stream);
3092 setup_menu (ModestMsgViewWindow *self)
3094 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3096 /* Settings menu buttons */
3097 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"),
3098 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3099 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3100 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"),
3101 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3102 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3104 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"),
3105 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3106 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3107 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"),
3108 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3109 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3111 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"),
3112 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3113 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3114 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"),
3115 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3116 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3118 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_newemail"),
3119 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3120 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3121 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"),
3122 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3123 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3127 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3129 ModestMsgViewWindowPrivate *priv;
3130 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3131 GSList *recipients = NULL;
3133 gboolean contacts_to_add = FALSE;
3135 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3136 if (msg == NULL) return;
3137 recipients = modest_tny_msg_get_all_recipients_list (msg);
3139 if (recipients != NULL) {
3140 GtkWidget *picker_dialog;
3141 GtkWidget *selector;
3143 gchar *selected = NULL;
3145 selector = hildon_touch_selector_new_text ();
3146 g_object_ref (selector);
3148 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3149 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3150 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3151 (const gchar *) node->data);
3152 contacts_to_add = TRUE;
3156 if (contacts_to_add) {
3159 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3160 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3162 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3163 HILDON_TOUCH_SELECTOR (selector));
3165 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3167 if (picker_result == GTK_RESPONSE_OK) {
3168 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3170 gtk_widget_destroy (picker_dialog);
3173 modest_address_book_add_address (selected);
3178 g_object_unref (selector);
3183 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3184 g_object_unref (msg);