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;
738 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
739 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
742 /* Add common dimming rules */
743 modest_dimming_rules_group_add_rules (toolbar_rules_group,
744 modest_msg_view_toolbar_dimming_entries,
745 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
746 MODEST_WINDOW (self));
747 modest_dimming_rules_group_add_rules (clipboard_rules_group,
748 modest_msg_view_clipboard_dimming_entries,
749 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
750 MODEST_WINDOW (self));
752 /* Insert dimming rules group for this window */
753 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
754 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
755 g_object_unref (toolbar_rules_group);
756 g_object_unref (clipboard_rules_group);
758 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
760 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);
761 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
762 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
763 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
764 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
765 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
766 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
767 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
768 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
769 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
770 G_CALLBACK (modest_ui_actions_on_details), obj);
771 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
772 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
773 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
774 G_CALLBACK (on_fetch_image), obj);
776 g_signal_connect (G_OBJECT (obj), "key-release-event",
777 G_CALLBACK (modest_msg_view_window_key_event),
780 g_signal_connect (G_OBJECT (obj), "key-press-event",
781 G_CALLBACK (modest_msg_view_window_key_event),
784 g_signal_connect (G_OBJECT (obj), "move-focus",
785 G_CALLBACK (on_move_focus), obj);
787 /* Mail Operation Queue */
788 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
790 G_CALLBACK (on_queue_changed),
793 /* Account manager */
794 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
796 G_CALLBACK(on_account_removed),
799 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
801 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
802 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
803 priv->last_search = NULL;
805 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
807 /* Init the clipboard actions dim status */
808 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
810 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
815 /* FIXME: parameter checks */
817 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
818 const gchar *modest_account_name,
819 const gchar *msg_uid,
821 GtkTreeRowReference *row_reference)
823 ModestMsgViewWindow *window = NULL;
824 ModestMsgViewWindowPrivate *priv = NULL;
825 TnyFolder *header_folder = NULL;
826 ModestHeaderView *header_view = NULL;
827 ModestWindow *main_window = NULL;
828 ModestWindowMgr *mgr = NULL;
831 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
834 mgr = modest_runtime_get_window_mgr ();
835 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
836 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
838 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
840 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
842 /* Remember the message list's TreeModel so we can detect changes
843 * and change the list selection when necessary: */
845 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
847 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
848 MODEST_MAIN_WINDOW(main_window),
849 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
852 if (header_view != NULL){
853 header_folder = modest_header_view_get_folder(header_view);
854 /* This could happen if the header folder was
855 unseleted before opening this msg window (for
856 example if the user selects an account in the
857 folder view of the main window */
859 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
860 priv->header_folder_id = tny_folder_get_id(header_folder);
861 g_assert(priv->header_folder_id != NULL);
862 g_object_unref(header_folder);
866 /* Setup row references and connect signals */
867 priv->header_model = g_object_ref (model);
870 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
871 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
872 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
874 priv->row_reference = NULL;
875 priv->next_row_reference = NULL;
878 /* Connect signals */
879 priv->row_changed_handler =
880 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
881 G_CALLBACK(modest_msg_view_window_on_row_changed),
883 priv->row_deleted_handler =
884 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
885 G_CALLBACK(modest_msg_view_window_on_row_deleted),
887 priv->row_inserted_handler =
888 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
889 G_CALLBACK(modest_msg_view_window_on_row_inserted),
891 priv->rows_reordered_handler =
892 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
893 G_CALLBACK(modest_msg_view_window_on_row_reordered),
896 if (header_view != NULL){
897 modest_header_view_add_observer(header_view,
898 MODEST_HEADER_VIEW_OBSERVER(window));
901 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
902 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
904 /* gtk_widget_show_all (GTK_WIDGET (window)); */
905 modest_msg_view_window_update_priority (window);
906 /* Check dimming rules */
907 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
908 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
909 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
911 return MODEST_WINDOW(window);
915 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
916 const gchar *modest_account_name,
917 const gchar *msg_uid,
918 GtkTreeRowReference *row_reference)
920 ModestMsgViewWindow *window = NULL;
921 ModestMsgViewWindowPrivate *priv = NULL;
922 TnyFolder *header_folder = NULL;
923 ModestWindowMgr *mgr = NULL;
927 mgr = modest_runtime_get_window_mgr ();
928 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
929 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
931 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
933 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
935 /* Remember the message list's TreeModel so we can detect changes
936 * and change the list selection when necessary: */
938 if (header_view != NULL){
939 header_folder = modest_header_view_get_folder(header_view);
940 /* This could happen if the header folder was
941 unseleted before opening this msg window (for
942 example if the user selects an account in the
943 folder view of the main window */
945 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
946 priv->header_folder_id = tny_folder_get_id(header_folder);
947 g_assert(priv->header_folder_id != NULL);
948 g_object_unref(header_folder);
952 /* Setup row references and connect signals */
953 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
954 g_object_ref (priv->header_model);
957 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
958 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
959 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
961 priv->row_reference = NULL;
962 priv->next_row_reference = NULL;
965 /* Connect signals */
966 priv->row_changed_handler =
967 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
968 G_CALLBACK(modest_msg_view_window_on_row_changed),
970 priv->row_deleted_handler =
971 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
972 G_CALLBACK(modest_msg_view_window_on_row_deleted),
974 priv->row_inserted_handler =
975 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
976 G_CALLBACK(modest_msg_view_window_on_row_inserted),
978 priv->rows_reordered_handler =
979 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
980 G_CALLBACK(modest_msg_view_window_on_row_reordered),
983 if (header_view != NULL){
984 modest_header_view_add_observer(header_view,
985 MODEST_HEADER_VIEW_OBSERVER(window));
988 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
990 path = gtk_tree_row_reference_get_path (row_reference);
991 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
993 gtk_tree_model_get (priv->header_model, &iter,
994 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
996 message_reader (window, priv, header, row_reference);
998 gtk_tree_path_free (path);
1000 /* Check dimming rules */
1001 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1002 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1003 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1005 return MODEST_WINDOW(window);
1009 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1010 const gchar *modest_account_name,
1011 const gchar *msg_uid)
1013 ModestMsgViewWindow *window = NULL;
1014 ModestMsgViewWindowPrivate *priv = NULL;
1015 ModestWindowMgr *mgr = NULL;
1017 mgr = modest_runtime_get_window_mgr ();
1018 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1019 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1020 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1022 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1024 /* Remember that this is a search result,
1025 * so we can disable some UI appropriately: */
1026 priv->is_search_result = TRUE;
1028 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1030 update_window_title (window);
1031 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1032 modest_msg_view_window_update_priority (window);
1034 /* Check dimming rules */
1035 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1036 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1037 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1039 return MODEST_WINDOW(window);
1043 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1044 const gchar *modest_account_name,
1045 const gchar *msg_uid)
1047 GObject *obj = NULL;
1048 ModestMsgViewWindowPrivate *priv;
1049 ModestWindowMgr *mgr = NULL;
1051 g_return_val_if_fail (msg, NULL);
1052 mgr = modest_runtime_get_window_mgr ();
1053 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1054 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1055 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1056 modest_account_name, msg_uid);
1058 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1059 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1061 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1063 /* Check dimming rules */
1064 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1065 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1066 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1068 return MODEST_WINDOW(obj);
1072 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1075 ModestMsgViewWindow *window)
1077 check_dimming_rules_after_change (window);
1081 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1083 ModestMsgViewWindow *window)
1085 check_dimming_rules_after_change (window);
1087 /* The window could have dissapeared */
1090 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1092 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1093 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1097 /* On insertions we check if the folder still has the message we are
1098 * showing or do not. If do not, we do nothing. Which means we are still
1099 * not attached to any header folder and thus next/prev buttons are
1100 * still dimmed. Once the message that is shown by msg-view is found, the
1101 * new model of header-view will be attached and the references will be set.
1102 * On each further insertions dimming rules will be checked. However
1103 * this requires extra CPU time at least works.
1104 * (An message might be deleted from TnyFolder and thus will not be
1105 * inserted into the model again for example if it is removed by the
1106 * imap server and the header view is refreshed.)
1109 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1110 GtkTreePath *tree_path,
1111 GtkTreeIter *tree_iter,
1112 ModestMsgViewWindow *window)
1114 ModestMsgViewWindowPrivate *priv = NULL;
1115 TnyHeader *header = NULL;
1117 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1118 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1120 g_assert (model == priv->header_model);
1122 /* Check if the newly inserted message is the same we are actually
1123 * showing. IF not, we should remain detached from the header model
1124 * and thus prev and next toolbar buttons should remain dimmed. */
1125 gtk_tree_model_get (model, tree_iter,
1126 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1129 if (TNY_IS_HEADER (header)) {
1132 uid = modest_tny_folder_get_header_unique_id (header);
1133 if (!g_str_equal(priv->msg_uid, uid)) {
1134 check_dimming_rules_after_change (window);
1136 g_object_unref (G_OBJECT(header));
1140 g_object_unref(G_OBJECT(header));
1143 if (priv->row_reference) {
1144 gtk_tree_row_reference_free (priv->row_reference);
1147 /* Setup row_reference for the actual msg. */
1148 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1149 if (priv->row_reference == NULL) {
1150 g_warning("No reference for msg header item.");
1154 /* Now set up next_row_reference. */
1155 if (priv->next_row_reference) {
1156 gtk_tree_row_reference_free (priv->next_row_reference);
1159 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1160 select_next_valid_row (priv->header_model,
1161 &(priv->next_row_reference), FALSE, priv->is_outbox);
1163 /* Connect the remaining callbacks to become able to detect
1164 * changes in header-view. */
1165 priv->row_changed_handler =
1166 g_signal_connect (priv->header_model, "row-changed",
1167 G_CALLBACK (modest_msg_view_window_on_row_changed),
1169 priv->row_deleted_handler =
1170 g_signal_connect (priv->header_model, "row-deleted",
1171 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1173 priv->rows_reordered_handler =
1174 g_signal_connect (priv->header_model, "rows-reordered",
1175 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1178 check_dimming_rules_after_change (window);
1182 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1186 ModestMsgViewWindow *window)
1188 ModestMsgViewWindowPrivate *priv = NULL;
1189 gboolean already_changed = FALSE;
1191 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1193 /* If the current row was reordered select the proper next
1194 valid row. The same if the next row reference changes */
1195 if (priv->row_reference &&
1196 gtk_tree_row_reference_valid (priv->row_reference)) {
1198 path = gtk_tree_row_reference_get_path (priv->row_reference);
1199 if (gtk_tree_path_compare (path, arg1) == 0) {
1200 if (priv->next_row_reference) {
1201 gtk_tree_row_reference_free (priv->next_row_reference);
1203 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1204 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1205 already_changed = TRUE;
1207 gtk_tree_path_free (path);
1209 if (!already_changed &&
1210 priv->next_row_reference &&
1211 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1213 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1214 if (gtk_tree_path_compare (path, arg1) == 0) {
1215 if (priv->next_row_reference) {
1216 gtk_tree_row_reference_free (priv->next_row_reference);
1218 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1219 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1221 gtk_tree_path_free (path);
1223 check_dimming_rules_after_change (window);
1226 /* The modest_msg_view_window_update_model_replaced implements update
1227 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1228 * actually belongs to the header-view is the same as the TnyFolder of
1229 * the message of msg-view or not. If they are different, there is
1230 * nothing to do. If they are the same, then the model has replaced and
1231 * the reference in msg-view shall be replaced from the old model to
1232 * the new model. In this case the view will be detached from it's
1233 * header folder. From this point the next/prev buttons are dimmed.
1236 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1237 GtkTreeModel *model,
1238 const gchar *tny_folder_id)
1240 ModestMsgViewWindowPrivate *priv = NULL;
1241 ModestMsgViewWindow *window = NULL;
1243 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1244 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1246 window = MODEST_MSG_VIEW_WINDOW(observer);
1247 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1249 /* If there is an other folder in the header-view then we do
1250 * not care about it's model (msg list). Else if the
1251 * header-view shows the folder the msg shown by us is in, we
1252 * shall replace our model reference and make some check. */
1253 if(model == NULL || tny_folder_id == NULL ||
1254 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1257 /* Model is changed(replaced), so we should forget the old
1258 * one. Because there might be other references and there
1259 * might be some change on the model even if we unreferenced
1260 * it, we need to disconnect our signals here. */
1261 if (priv->header_model) {
1262 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1263 priv->row_changed_handler))
1264 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1265 priv->row_changed_handler);
1266 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1267 priv->row_deleted_handler))
1268 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1269 priv->row_deleted_handler);
1270 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1271 priv->row_inserted_handler))
1272 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1273 priv->row_inserted_handler);
1274 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1275 priv->rows_reordered_handler))
1276 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1277 priv->rows_reordered_handler);
1280 if (priv->row_reference)
1281 gtk_tree_row_reference_free (priv->row_reference);
1282 if (priv->next_row_reference)
1283 gtk_tree_row_reference_free (priv->next_row_reference);
1284 g_object_unref(priv->header_model);
1287 priv->row_changed_handler = 0;
1288 priv->row_deleted_handler = 0;
1289 priv->row_inserted_handler = 0;
1290 priv->rows_reordered_handler = 0;
1291 priv->next_row_reference = NULL;
1292 priv->row_reference = NULL;
1293 priv->header_model = NULL;
1296 priv->header_model = g_object_ref (model);
1298 /* Also we must connect to the new model for row insertions.
1299 * Only for insertions now. We will need other ones only after
1300 * the msg is show by msg-view is added to the new model. */
1301 priv->row_inserted_handler =
1302 g_signal_connect (priv->header_model, "row-inserted",
1303 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1306 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1307 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1311 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1313 ModestMsgViewWindowPrivate *priv= NULL;
1315 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1316 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1318 return priv->progress_hint;
1322 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1324 ModestMsgViewWindowPrivate *priv= NULL;
1326 TnyHeader *header = NULL;
1327 GtkTreePath *path = NULL;
1330 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1331 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1333 /* If the message was not obtained from a treemodel,
1334 * for instance if it was opened directly by the search UI:
1336 if (priv->header_model == NULL ||
1337 priv->row_reference == NULL ||
1338 !gtk_tree_row_reference_valid (priv->row_reference)) {
1339 msg = modest_msg_view_window_get_message (self);
1341 header = tny_msg_get_header (msg);
1342 g_object_unref (msg);
1347 /* Get iter of the currently selected message in the header view: */
1348 path = gtk_tree_row_reference_get_path (priv->row_reference);
1349 g_return_val_if_fail (path != NULL, NULL);
1350 gtk_tree_model_get_iter (priv->header_model,
1354 /* Get current message header */
1355 gtk_tree_model_get (priv->header_model, &iter,
1356 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1359 gtk_tree_path_free (path);
1364 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1366 ModestMsgViewWindowPrivate *priv;
1368 g_return_val_if_fail (self, NULL);
1370 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1372 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1376 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1378 ModestMsgViewWindowPrivate *priv;
1380 g_return_val_if_fail (self, NULL);
1382 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1384 return (const gchar*) priv->msg_uid;
1388 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1391 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1392 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1393 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1397 is_active = gtk_toggle_action_get_active (toggle);
1400 gtk_widget_show (priv->find_toolbar);
1401 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1403 gtk_widget_hide (priv->find_toolbar);
1404 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1407 /* update the toggle buttons status */
1408 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1410 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1415 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1416 ModestMsgViewWindow *obj)
1418 GtkToggleAction *toggle;
1419 ModestWindowPrivate *parent_priv;
1420 ModestMsgViewWindowPrivate *priv;
1422 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1423 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1425 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1426 gtk_toggle_action_set_active (toggle, FALSE);
1427 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1431 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1432 ModestMsgViewWindow *obj)
1434 gchar *current_search;
1435 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1437 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1438 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1442 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1444 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1445 g_free (current_search);
1446 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1450 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1452 g_free (priv->last_search);
1453 priv->last_search = g_strdup (current_search);
1454 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1457 hildon_banner_show_information (NULL, NULL,
1458 _HL("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,
1468 _HL("ckct_ib_find_search_complete"));
1469 g_free (priv->last_search);
1470 priv->last_search = NULL;
1472 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1473 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1477 g_free (current_search);
1482 modest_msg_view_window_set_zoom (ModestWindow *window,
1485 ModestMsgViewWindowPrivate *priv;
1486 ModestWindowPrivate *parent_priv;
1488 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1490 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1491 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1492 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1497 modest_msg_view_window_get_zoom (ModestWindow *window)
1499 ModestMsgViewWindowPrivate *priv;
1501 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1503 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1504 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1508 modest_msg_view_window_zoom_plus (ModestWindow *window)
1511 ModestMsgViewWindowPrivate *priv;
1515 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1516 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1518 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1520 if (zoom_level >= 2.0) {
1521 hildon_banner_show_information (NULL, NULL,
1522 _CS("ckct_ib_max_zoom_level_reached"));
1524 } else if (zoom_level >= 1.5) {
1526 } else if (zoom_level >= 1.2) {
1528 } else if (zoom_level >= 1.0) {
1530 } else if (zoom_level >= 0.8) {
1532 } else if (zoom_level >= 0.5) {
1538 /* set zoom level */
1539 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1540 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1541 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1542 g_free (banner_text);
1543 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1549 modest_msg_view_window_zoom_minus (ModestWindow *window)
1552 ModestMsgViewWindowPrivate *priv;
1556 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1557 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1559 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1561 if (zoom_level <= 0.5) {
1562 hildon_banner_show_information (NULL, NULL,
1563 _CS("ckct_ib_min_zoom_level_reached"));
1565 } else if (zoom_level <= 0.8) {
1567 } else if (zoom_level <= 1.0) {
1569 } else if (zoom_level <= 1.2) {
1571 } else if (zoom_level <= 1.5) {
1573 } else if (zoom_level <= 2.0) {
1579 /* set zoom level */
1580 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1581 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1582 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1583 g_free (banner_text);
1584 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1591 modest_msg_view_window_key_event (GtkWidget *window,
1597 focus = gtk_window_get_focus (GTK_WINDOW (window));
1599 /* for the find toolbar case */
1600 if (focus && GTK_IS_ENTRY (focus)) {
1601 if (event->keyval == GDK_BackSpace) {
1603 copy = gdk_event_copy ((GdkEvent *) event);
1604 gtk_widget_event (focus, copy);
1605 gdk_event_free (copy);
1610 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1611 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1612 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1613 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1614 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1615 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1616 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1617 /* gboolean return_value; */
1619 if (event->type == GDK_KEY_PRESS) {
1620 GtkScrollType scroll_type;
1622 switch (event->keyval) {
1625 scroll_type = GTK_SCROLL_STEP_UP; break;
1628 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1630 case GDK_KP_Page_Up:
1631 scroll_type = GTK_SCROLL_PAGE_UP; break;
1633 case GDK_KP_Page_Down:
1634 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1637 scroll_type = GTK_SCROLL_START; break;
1640 scroll_type = GTK_SCROLL_END; break;
1641 default: scroll_type = GTK_SCROLL_NONE;
1644 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1645 /* scroll_type, FALSE, &return_value); */
1656 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1659 ModestMsgViewWindowPrivate *priv;
1660 GtkTreeIter tmp_iter;
1661 gboolean is_last_selected;
1663 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1664 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1666 /*if no model (so no rows at all), then virtually we are the last*/
1667 if (!priv->header_model || !priv->row_reference)
1670 if (!gtk_tree_row_reference_valid (priv->row_reference))
1673 path = gtk_tree_row_reference_get_path (priv->row_reference);
1677 is_last_selected = TRUE;
1678 while (is_last_selected) {
1680 gtk_tree_path_next (path);
1681 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1683 gtk_tree_model_get (priv->header_model, &tmp_iter,
1684 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1687 if (msg_is_visible (header, priv->is_outbox))
1688 is_last_selected = FALSE;
1689 g_object_unref(G_OBJECT(header));
1692 gtk_tree_path_free (path);
1693 return is_last_selected;
1697 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1699 ModestMsgViewWindowPrivate *priv;
1701 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1702 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1704 return priv->header_model != NULL;
1708 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1710 ModestMsgViewWindowPrivate *priv;
1712 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1713 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1715 return priv->is_search_result;
1719 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1721 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1723 if (!check_outbox) {
1726 ModestTnySendQueueStatus status;
1727 status = modest_tny_all_send_queues_get_msg_status (header);
1728 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1729 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1734 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1737 ModestMsgViewWindowPrivate *priv;
1738 gboolean is_first_selected;
1739 GtkTreeIter tmp_iter;
1741 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1742 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1744 /*if no model (so no rows at all), then virtually we are the first*/
1745 if (!priv->header_model || !priv->row_reference)
1748 if (!gtk_tree_row_reference_valid (priv->row_reference))
1751 path = gtk_tree_row_reference_get_path (priv->row_reference);
1755 is_first_selected = TRUE;
1756 while (is_first_selected) {
1758 if(!gtk_tree_path_prev (path))
1760 /* Here the 'if' is needless for logic, but let make sure
1761 * iter is valid for gtk_tree_model_get. */
1762 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1764 gtk_tree_model_get (priv->header_model, &tmp_iter,
1765 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1768 if (msg_is_visible (header, priv->is_outbox))
1769 is_first_selected = FALSE;
1770 g_object_unref(G_OBJECT(header));
1773 gtk_tree_path_free (path);
1774 return is_first_selected;
1779 GtkTreeRowReference *row_reference;
1783 message_reader_performer (gboolean canceled,
1785 GtkWindow *parent_window,
1786 TnyAccount *account,
1789 ModestMailOperation *mail_op = NULL;
1790 MsgReaderInfo *info;
1792 info = (MsgReaderInfo *) user_data;
1793 if (canceled || err) {
1797 /* Register the header - it'll be unregistered in the callback */
1798 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1800 /* New mail operation */
1801 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1802 modest_ui_actions_disk_operations_error_handler,
1805 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1806 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1807 g_object_unref (mail_op);
1809 /* Update dimming rules */
1810 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1811 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1814 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1815 g_object_unref (info->header);
1816 g_slice_free (MsgReaderInfo, info);
1821 * Reads the message whose summary item is @header. It takes care of
1822 * several things, among others:
1824 * If the message was not previously downloaded then ask the user
1825 * before downloading. If there is no connection launch the connection
1826 * dialog. Update toolbar dimming rules.
1828 * Returns: TRUE if the mail operation was started, otherwise if the
1829 * user do not want to download the message, or if the user do not
1830 * want to connect, then the operation is not issued
1833 message_reader (ModestMsgViewWindow *window,
1834 ModestMsgViewWindowPrivate *priv,
1836 GtkTreeRowReference *row_reference)
1838 ModestWindowMgr *mgr;
1839 TnyAccount *account;
1841 MsgReaderInfo *info;
1843 g_return_val_if_fail (row_reference != NULL, FALSE);
1845 mgr = modest_runtime_get_window_mgr ();
1846 /* Msg download completed */
1847 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1849 /* We set the header from model while we're loading */
1850 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1851 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1853 /* Ask the user if he wants to download the message if
1855 if (!tny_device_is_online (modest_runtime_get_device())) {
1856 GtkResponseType response;
1858 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1859 _("mcen_nc_get_msg"));
1860 if (response == GTK_RESPONSE_CANCEL)
1863 folder = tny_header_get_folder (header);
1864 info = g_slice_new (MsgReaderInfo);
1865 info->header = g_object_ref (header);
1866 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1868 /* Offer the connection dialog if necessary */
1869 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1871 TNY_FOLDER_STORE (folder),
1872 message_reader_performer,
1874 g_object_unref (folder);
1879 folder = tny_header_get_folder (header);
1880 account = tny_folder_get_account (folder);
1881 info = g_slice_new (MsgReaderInfo);
1882 info->header = g_object_ref (header);
1883 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1885 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1886 g_object_unref (account);
1887 g_object_unref (folder);
1893 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1895 ModestMsgViewWindowPrivate *priv;
1896 GtkTreePath *path= NULL;
1897 GtkTreeIter tmp_iter;
1899 gboolean retval = TRUE;
1900 GtkTreeRowReference *row_reference = NULL;
1902 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1903 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1905 if (!priv->row_reference)
1908 /* Update the next row reference if it's not valid. This could
1909 happen if for example the header which it was pointing to,
1910 was deleted. The best place to do it is in the row-deleted
1911 handler but the tinymail model do not work like the glib
1912 tree models and reports the deletion when the row is still
1914 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1915 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1916 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1917 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1920 if (priv->next_row_reference)
1921 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1925 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1927 gtk_tree_model_get_iter (priv->header_model,
1930 gtk_tree_path_free (path);
1932 gtk_tree_model_get (priv->header_model, &tmp_iter,
1933 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1936 /* Read the message & show it */
1937 if (!message_reader (window, priv, header, row_reference)) {
1940 gtk_tree_row_reference_free (row_reference);
1943 g_object_unref (header);
1949 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1951 ModestMsgViewWindowPrivate *priv = NULL;
1953 gboolean finished = FALSE;
1954 gboolean retval = FALSE;
1956 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1957 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1959 /* Return inmediatly if there is no header model */
1960 if (!priv->header_model || !priv->row_reference)
1963 path = gtk_tree_row_reference_get_path (priv->row_reference);
1964 while (!finished && gtk_tree_path_prev (path)) {
1968 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1969 gtk_tree_model_get (priv->header_model, &iter,
1970 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1974 if (msg_is_visible (header, priv->is_outbox)) {
1975 GtkTreeRowReference *row_reference;
1976 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1977 /* Read the message & show it */
1978 retval = message_reader (window, priv, header, row_reference);
1979 gtk_tree_row_reference_free (row_reference);
1983 g_object_unref (header);
1987 gtk_tree_path_free (path);
1992 view_msg_cb (ModestMailOperation *mail_op,
1999 ModestMsgViewWindow *self = NULL;
2000 ModestMsgViewWindowPrivate *priv = NULL;
2001 GtkTreeRowReference *row_reference = NULL;
2003 /* Unregister the header (it was registered before creating the mail operation) */
2004 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2006 row_reference = (GtkTreeRowReference *) user_data;
2008 gtk_tree_row_reference_free (row_reference);
2012 /* If there was any error */
2013 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2014 gtk_tree_row_reference_free (row_reference);
2018 /* Get the window */
2019 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2020 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2021 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2023 /* Update the row reference */
2024 if (priv->row_reference != NULL) {
2025 gtk_tree_row_reference_free (priv->row_reference);
2026 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2027 if (priv->next_row_reference != NULL) {
2028 gtk_tree_row_reference_free (priv->next_row_reference);
2030 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2031 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2034 /* Mark header as read */
2035 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2036 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2038 /* Set new message */
2039 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2040 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2041 modest_msg_view_window_update_priority (self);
2042 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2043 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2046 /* Set the new message uid of the window */
2047 if (priv->msg_uid) {
2048 g_free (priv->msg_uid);
2049 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2052 /* Notify the observers */
2053 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2054 0, priv->header_model, priv->row_reference);
2057 g_object_unref (self);
2058 gtk_tree_row_reference_free (row_reference);
2062 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2064 ModestMsgViewWindowPrivate *priv;
2066 TnyFolderType folder_type;
2068 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2070 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2072 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2076 folder = tny_msg_get_folder (msg);
2078 folder_type = modest_tny_folder_guess_folder_type (folder);
2079 g_object_unref (folder);
2081 g_object_unref (msg);
2089 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2091 ModestMsgViewWindowPrivate *priv;
2092 TnyHeader *header = NULL;
2093 TnyHeaderFlags flags = 0;
2095 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2097 if (priv->header_model && priv->row_reference) {
2099 GtkTreePath *path = NULL;
2101 path = gtk_tree_row_reference_get_path (priv->row_reference);
2102 g_return_if_fail (path != NULL);
2103 gtk_tree_model_get_iter (priv->header_model,
2105 gtk_tree_row_reference_get_path (priv->row_reference));
2107 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2109 gtk_tree_path_free (path);
2112 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2114 header = tny_msg_get_header (msg);
2115 g_object_unref (msg);
2120 flags = tny_header_get_flags (header);
2121 g_object_unref(G_OBJECT(header));
2124 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2129 toolbar_resize (ModestMsgViewWindow *self)
2131 ModestMsgViewWindowPrivate *priv = NULL;
2132 ModestWindowPrivate *parent_priv = NULL;
2134 gint static_button_size;
2135 ModestWindowMgr *mgr;
2137 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2138 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2139 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2141 mgr = modest_runtime_get_window_mgr ();
2142 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2144 if (parent_priv->toolbar) {
2145 /* left size buttons */
2146 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2147 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2148 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2149 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2150 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2151 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2152 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2153 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2154 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2155 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2156 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2157 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2158 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2159 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2160 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2161 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2163 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2164 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2165 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2166 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2172 modest_msg_view_window_show_toolbar (ModestWindow *self,
2173 gboolean show_toolbar)
2175 ModestMsgViewWindowPrivate *priv = NULL;
2176 ModestWindowPrivate *parent_priv;
2177 GtkWidget *reply_button = NULL, *menu = NULL;
2179 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2180 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2182 /* Set optimized view status */
2183 priv->optimized_view = !show_toolbar;
2185 if (!parent_priv->toolbar) {
2186 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2188 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2190 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2191 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2192 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2195 hildon_window_add_toolbar (HILDON_WINDOW (self),
2196 GTK_TOOLBAR (parent_priv->toolbar));
2198 /* Set reply button tap and hold menu */
2199 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2200 "/ToolBar/ToolbarMessageReply");
2201 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2202 "/ToolbarReplyCSM");
2203 if (menu && reply_button)
2204 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2208 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2209 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2210 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2212 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2213 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2214 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2216 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2219 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2220 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2225 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2227 ModestMsgViewWindow *window)
2229 if (!GTK_WIDGET_VISIBLE (window))
2232 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2236 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2238 ModestMsgViewWindowPrivate *priv;
2240 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2241 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2243 return priv->progress_hint;
2247 observers_empty (ModestMsgViewWindow *self)
2250 ModestMsgViewWindowPrivate *priv;
2251 gboolean is_empty = TRUE;
2252 guint pending_ops = 0;
2254 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2255 tmp = priv->progress_widgets;
2257 /* Check all observers */
2258 while (tmp && is_empty) {
2259 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2260 is_empty = pending_ops == 0;
2262 tmp = g_slist_next(tmp);
2269 on_account_removed (TnyAccountStore *account_store,
2270 TnyAccount *account,
2273 /* Do nothing if it's a transport account, because we only
2274 show the messages of a store account */
2275 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2276 const gchar *parent_acc = NULL;
2277 const gchar *our_acc = NULL;
2279 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2280 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2282 /* Close this window if I'm showing a message of the removed account */
2283 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2284 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2289 on_mail_operation_started (ModestMailOperation *mail_op,
2292 ModestMsgViewWindow *self;
2293 ModestMailOperationTypeOperation op_type;
2295 ModestMsgViewWindowPrivate *priv;
2296 GObject *source = NULL;
2298 self = MODEST_MSG_VIEW_WINDOW (user_data);
2299 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2300 op_type = modest_mail_operation_get_type_operation (mail_op);
2301 tmp = priv->progress_widgets;
2302 source = modest_mail_operation_get_source(mail_op);
2303 if (G_OBJECT (self) == source) {
2304 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2305 set_toolbar_transfer_mode(self);
2307 modest_progress_object_add_operation (
2308 MODEST_PROGRESS_OBJECT (tmp->data),
2310 tmp = g_slist_next (tmp);
2314 g_object_unref (source);
2318 on_mail_operation_finished (ModestMailOperation *mail_op,
2321 ModestMsgViewWindow *self;
2322 ModestMailOperationTypeOperation op_type;
2324 ModestMsgViewWindowPrivate *priv;
2326 self = MODEST_MSG_VIEW_WINDOW (user_data);
2327 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2328 op_type = modest_mail_operation_get_type_operation (mail_op);
2329 tmp = priv->progress_widgets;
2331 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2333 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2335 tmp = g_slist_next (tmp);
2338 /* If no more operations are being observed, NORMAL mode is enabled again */
2339 if (observers_empty (self)) {
2340 set_progress_hint (self, FALSE);
2343 /* Update dimming rules. We have to do this right here
2344 and not in view_msg_cb because at that point the
2345 transfer mode is still enabled so the dimming rule
2346 won't let the user delete the message that has been
2347 readed for example */
2348 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2349 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2354 on_queue_changed (ModestMailOperationQueue *queue,
2355 ModestMailOperation *mail_op,
2356 ModestMailOperationQueueNotification type,
2357 ModestMsgViewWindow *self)
2359 ModestMsgViewWindowPrivate *priv;
2361 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2363 /* If this operations was created by another window, do nothing */
2364 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2367 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2368 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2370 "operation-started",
2371 G_CALLBACK (on_mail_operation_started),
2373 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2375 "operation-finished",
2376 G_CALLBACK (on_mail_operation_finished),
2378 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2379 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2381 "operation-started");
2382 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2384 "operation-finished");
2389 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2391 ModestMsgViewWindowPrivate *priv;
2392 TnyList *selected_attachments = NULL;
2394 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2395 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2397 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2398 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2400 return selected_attachments;
2406 guint banner_idle_id;
2407 } DecodeAsyncHelper;
2410 decode_async_banner_idle (gpointer user_data)
2412 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2414 helper->banner_idle_id = 0;
2415 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2421 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2427 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2429 if (helper->banner_idle_id > 0) {
2430 g_source_remove (helper->banner_idle_id);
2431 helper->banner_idle_id = 0;
2433 if (helper->banner) {
2434 gtk_widget_destroy (helper->banner);
2435 helper->banner = NULL;
2437 if (cancelled || err) {
2438 modest_platform_information_banner (NULL, NULL,
2439 _("mail_ib_file_operation_failed"));
2443 /* make the file read-only */
2444 g_chmod(helper->filepath, 0444);
2446 /* Activate the file */
2447 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2451 g_free (helper->filepath);
2452 g_slice_free (DecodeAsyncHelper, helper);
2456 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2457 TnyMimePart *mime_part)
2459 ModestMsgViewWindowPrivate *priv;
2460 const gchar *msg_uid;
2461 gchar *attachment_uid = NULL;
2462 gint attachment_index = 0;
2463 TnyList *attachments;
2465 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2466 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2467 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2469 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2470 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2471 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2472 g_object_unref (attachments);
2474 if (msg_uid && attachment_index >= 0) {
2475 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2478 if (mime_part == NULL) {
2479 gboolean error = FALSE;
2480 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2481 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2483 } else if (tny_list_get_length (selected_attachments) > 1) {
2484 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2488 iter = tny_list_create_iterator (selected_attachments);
2489 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2490 g_object_unref (iter);
2492 g_object_unref (selected_attachments);
2497 g_object_ref (mime_part);
2500 if (tny_mime_part_is_purged (mime_part)) {
2501 g_object_unref (mime_part);
2505 if (!modest_tny_mime_part_is_msg (mime_part)) {
2506 gchar *filepath = NULL;
2507 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2508 gboolean show_error_banner = FALSE;
2509 TnyFsStream *temp_stream = NULL;
2510 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2513 if (temp_stream != NULL) {
2514 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2515 helper->filepath = g_strdup (filepath);
2516 helper->banner = NULL;
2517 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2518 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2519 on_decode_to_stream_async_handler,
2522 g_object_unref (temp_stream);
2523 /* NOTE: files in the temporary area will be automatically
2524 * cleaned after some time if they are no longer in use */
2527 const gchar *content_type;
2528 /* the file may already exist but it isn't writable,
2529 * let's try to open it anyway */
2530 content_type = tny_mime_part_get_content_type (mime_part);
2531 modest_platform_activate_file (filepath, content_type);
2533 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2534 show_error_banner = TRUE;
2539 if (show_error_banner)
2540 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2542 /* message attachment */
2543 TnyHeader *header = NULL;
2544 ModestWindowMgr *mgr;
2545 ModestWindow *msg_win = NULL;
2548 header = tny_msg_get_header (TNY_MSG (mime_part));
2549 mgr = modest_runtime_get_window_mgr ();
2550 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2553 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2554 * thus, we don't do anything */
2555 g_warning ("window for is already being created");
2557 /* it's not found, so create a new window for it */
2558 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2559 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2561 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2562 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2563 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2564 modest_window_get_zoom (MODEST_WINDOW (window)));
2565 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2566 gtk_widget_show_all (GTK_WIDGET (msg_win));
2569 g_object_unref (mime_part);
2582 GnomeVFSResult result;
2585 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2586 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2587 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2588 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2591 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2595 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2596 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2597 g_free (pair->filename);
2598 g_object_unref (pair->part);
2599 g_slice_free (SaveMimePartPair, pair);
2601 g_list_free (info->pairs);
2604 gtk_widget_destroy (info->banner);
2605 g_slice_free (SaveMimePartInfo, info);
2610 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2612 if (info->pairs != NULL) {
2613 save_mime_part_to_file (info);
2615 /* This is a GDK lock because we are an idle callback and
2616 * hildon_banner_show_information is or does Gtk+ code */
2618 gdk_threads_enter (); /* CHECKED */
2619 save_mime_part_info_free (info, TRUE);
2620 if (info->result == GNOME_VFS_OK) {
2621 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2622 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2623 hildon_banner_show_information (NULL, NULL,
2624 _KR("cerm_device_memory_full"));
2626 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2628 gdk_threads_leave (); /* CHECKED */
2635 save_mime_part_to_file (SaveMimePartInfo *info)
2637 GnomeVFSHandle *handle;
2639 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2641 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2642 if (info->result == GNOME_VFS_OK) {
2643 GError *error = NULL;
2644 stream = tny_vfs_stream_new (handle);
2645 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2646 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2648 info->result = GNOME_VFS_ERROR_IO;
2650 g_object_unref (G_OBJECT (stream));
2651 g_object_unref (pair->part);
2652 g_slice_free (SaveMimePartPair, pair);
2653 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2655 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2656 save_mime_part_info_free (info, FALSE);
2659 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2664 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2666 gboolean is_ok = TRUE;
2667 gint replaced_files = 0;
2668 const GList *files = info->pairs;
2671 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2672 SaveMimePartPair *pair = iter->data;
2673 if (modest_utils_file_exists (pair->filename)) {
2677 if (replaced_files) {
2678 GtkWidget *confirm_overwrite_dialog;
2679 const gchar *message = (replaced_files == 1) ?
2680 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2681 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2682 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2685 gtk_widget_destroy (confirm_overwrite_dialog);
2689 save_mime_part_info_free (info, TRUE);
2691 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2692 _CS("sfil_ib_saving"));
2693 info->banner = banner;
2694 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2700 save_attachments_response (GtkDialog *dialog,
2704 TnyList *mime_parts;
2706 GList *files_to_save = NULL;
2708 mime_parts = TNY_LIST (user_data);
2710 if (arg1 != GTK_RESPONSE_OK)
2713 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2715 if (!modest_utils_folder_writable (chooser_uri)) {
2716 hildon_banner_show_information
2717 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2721 iter = tny_list_create_iterator (mime_parts);
2722 while (!tny_iterator_is_done (iter)) {
2723 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2725 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2726 !tny_mime_part_is_purged (mime_part) &&
2727 (tny_mime_part_get_filename (mime_part) != NULL)) {
2728 SaveMimePartPair *pair;
2730 pair = g_slice_new0 (SaveMimePartPair);
2732 if (tny_list_get_length (mime_parts) > 1) {
2734 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2735 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2738 pair->filename = g_strdup (chooser_uri);
2740 pair->part = mime_part;
2741 files_to_save = g_list_prepend (files_to_save, pair);
2743 tny_iterator_next (iter);
2745 g_object_unref (iter);
2747 g_free (chooser_uri);
2749 if (files_to_save != NULL) {
2750 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2751 info->pairs = files_to_save;
2752 info->result = TRUE;
2753 save_mime_parts_to_file_with_checks (info);
2757 /* Free and close the dialog */
2758 g_object_unref (mime_parts);
2759 gtk_widget_destroy (GTK_WIDGET (dialog));
2763 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2765 ModestMsgViewWindowPrivate *priv;
2766 GtkWidget *save_dialog = NULL;
2767 gchar *folder = NULL;
2768 gchar *filename = NULL;
2769 gchar *save_multiple_str = NULL;
2771 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2772 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2774 if (mime_parts == NULL) {
2775 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2776 * selection available */
2777 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2778 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2781 g_object_ref (mime_parts);
2784 /* prepare dialog */
2785 if (tny_list_get_length (mime_parts) == 1) {
2787 /* only one attachment selected */
2788 iter = tny_list_create_iterator (mime_parts);
2789 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2790 g_object_unref (iter);
2791 if (!modest_tny_mime_part_is_msg (mime_part) &&
2792 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2793 !tny_mime_part_is_purged (mime_part)) {
2794 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2796 /* TODO: show any error? */
2797 g_warning ("Tried to save a non-file attachment");
2798 g_object_unref (mime_parts);
2801 g_object_unref (mime_part);
2803 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2804 tny_list_get_length (mime_parts));
2807 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2808 GTK_FILE_CHOOSER_ACTION_SAVE);
2811 folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2812 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2817 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2822 /* if multiple, set multiple string */
2823 if (save_multiple_str) {
2824 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2825 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2828 /* We must run this asynchronously, because the hildon dialog
2829 performs a gtk_dialog_run by itself which leads to gdk
2831 g_signal_connect (save_dialog, "response",
2832 G_CALLBACK (save_attachments_response), mime_parts);
2834 gtk_widget_show_all (save_dialog);
2838 show_remove_attachment_information (gpointer userdata)
2840 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2841 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2843 /* We're outside the main lock */
2844 gdk_threads_enter ();
2846 if (priv->remove_attachment_banner != NULL) {
2847 gtk_widget_destroy (priv->remove_attachment_banner);
2848 g_object_unref (priv->remove_attachment_banner);
2851 priv->remove_attachment_banner = g_object_ref (
2852 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2854 gdk_threads_leave ();
2860 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2862 ModestMsgViewWindowPrivate *priv;
2863 TnyList *mime_parts = NULL;
2864 gchar *confirmation_message;
2870 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2871 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2873 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2874 * because we don't have selection
2876 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2878 /* Remove already purged messages from mime parts list */
2879 iter = tny_list_create_iterator (mime_parts);
2880 while (!tny_iterator_is_done (iter)) {
2881 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2882 tny_iterator_next (iter);
2883 if (tny_mime_part_is_purged (part)) {
2884 tny_list_remove (mime_parts, (GObject *) part);
2886 g_object_unref (part);
2888 g_object_unref (iter);
2890 if (tny_list_get_length (mime_parts) == 0) {
2891 g_object_unref (mime_parts);
2895 n_attachments = tny_list_get_length (mime_parts);
2896 if (n_attachments == 1) {
2900 iter = tny_list_create_iterator (mime_parts);
2901 part = (TnyMimePart *) tny_iterator_get_current (iter);
2902 g_object_unref (iter);
2903 if (modest_tny_mime_part_is_msg (part)) {
2905 header = tny_msg_get_header (TNY_MSG (part));
2906 filename = tny_header_dup_subject (header);
2907 g_object_unref (header);
2908 if (filename == NULL)
2909 filename = g_strdup (_("mail_va_no_subject"));
2911 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2913 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2915 g_object_unref (part);
2917 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2918 "mcen_nc_purge_files_text",
2919 n_attachments), n_attachments);
2921 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2922 confirmation_message);
2923 g_free (confirmation_message);
2925 if (response != GTK_RESPONSE_OK) {
2926 g_object_unref (mime_parts);
2930 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2932 iter = tny_list_create_iterator (mime_parts);
2933 while (!tny_iterator_is_done (iter)) {
2936 part = (TnyMimePart *) tny_iterator_get_current (iter);
2937 tny_mime_part_set_purged (TNY_MIME_PART (part));
2938 g_object_unref (part);
2939 tny_iterator_next (iter);
2941 g_object_unref (iter);
2943 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2944 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2945 tny_msg_rewrite_cache (msg);
2946 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2947 g_object_unref (msg);
2949 g_object_unref (mime_parts);
2951 if (priv->purge_timeout > 0) {
2952 g_source_remove (priv->purge_timeout);
2953 priv->purge_timeout = 0;
2956 if (priv->remove_attachment_banner) {
2957 gtk_widget_destroy (priv->remove_attachment_banner);
2958 g_object_unref (priv->remove_attachment_banner);
2959 priv->remove_attachment_banner = NULL;
2967 update_window_title (ModestMsgViewWindow *window)
2969 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2971 TnyHeader *header = NULL;
2972 gchar *subject = NULL;
2974 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2977 header = tny_msg_get_header (msg);
2978 subject = tny_header_dup_subject (header);
2979 g_object_unref (header);
2980 g_object_unref (msg);
2983 if ((subject == NULL)||(subject[0] == '\0')) {
2985 subject = g_strdup (_("mail_va_no_subject"));
2988 gtk_window_set_title (GTK_WINDOW (window), subject);
2992 static void on_move_focus (GtkWidget *widget,
2993 GtkDirectionType direction,
2996 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3000 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3002 GnomeVFSResult result;
3003 GnomeVFSHandle *handle = NULL;
3004 GnomeVFSFileInfo *info = NULL;
3007 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3008 if (result != GNOME_VFS_OK) {
3013 info = gnome_vfs_file_info_new ();
3014 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3015 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3016 /* We put a "safe" default size for going to cache */
3017 *expected_size = (300*1024);
3019 *expected_size = info->size;
3021 gnome_vfs_file_info_unref (info);
3023 stream = tny_vfs_stream_new (handle);
3032 TnyStream *output_stream;
3033 GtkWidget *msg_view;
3037 on_fetch_image_idle_refresh_view (gpointer userdata)
3040 FetchImageData *fidata = (FetchImageData *) userdata;
3041 g_message ("REFRESH VIEW");
3042 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3043 g_message ("QUEUING DRAW");
3044 gtk_widget_queue_draw (fidata->msg_view);
3046 g_object_unref (fidata->msg_view);
3047 g_slice_free (FetchImageData, fidata);
3052 on_fetch_image_thread (gpointer userdata)
3054 FetchImageData *fidata = (FetchImageData *) userdata;
3055 TnyStreamCache *cache;
3056 TnyStream *cache_stream;
3058 cache = modest_runtime_get_images_cache ();
3059 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3060 g_free (fidata->cache_id);
3061 g_free (fidata->uri);
3063 if (cache_stream != NULL) {
3064 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3065 tny_stream_close (cache_stream);
3066 g_object_unref (cache_stream);
3069 tny_stream_close (fidata->output_stream);
3070 g_object_unref (fidata->output_stream);
3073 gdk_threads_enter ();
3074 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3075 gdk_threads_leave ();
3081 on_fetch_image (ModestMsgView *msgview,
3084 ModestMsgViewWindow *window)
3086 const gchar *current_account;
3087 ModestMsgViewWindowPrivate *priv;
3088 FetchImageData *fidata;
3090 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3092 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3094 fidata = g_slice_new0 (FetchImageData);
3095 fidata->msg_view = g_object_ref (msgview);
3096 fidata->uri = g_strdup (uri);
3097 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3098 fidata->output_stream = g_object_ref (stream);
3100 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3101 g_object_unref (fidata->output_stream);
3102 g_free (fidata->cache_id);
3103 g_free (fidata->uri);
3104 g_object_unref (fidata->msg_view);
3105 g_slice_free (FetchImageData, fidata);
3106 tny_stream_close (stream);
3114 setup_menu (ModestMsgViewWindow *self)
3116 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3118 /* Settings menu buttons */
3119 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3120 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3121 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3122 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3123 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3124 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3126 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3127 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3128 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3129 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3130 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3131 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3133 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3134 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3135 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3136 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3137 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3138 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3140 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3141 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3142 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3143 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3144 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3145 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3149 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3151 ModestMsgViewWindowPrivate *priv;
3152 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3153 GSList *recipients = NULL;
3155 gboolean contacts_to_add = FALSE;
3157 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3161 header = modest_msg_view_window_get_header (self);
3164 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3165 g_object_unref (header);
3167 recipients = modest_tny_msg_get_all_recipients_list (msg);
3168 g_object_unref (msg);
3171 if (recipients != NULL) {
3172 GtkWidget *picker_dialog;
3173 GtkWidget *selector;
3175 gchar *selected = NULL;
3177 selector = hildon_touch_selector_new_text ();
3178 g_object_ref (selector);
3180 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3181 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3182 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3183 (const gchar *) node->data);
3184 contacts_to_add = TRUE;
3188 if (contacts_to_add) {
3191 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3192 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3194 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3195 HILDON_TOUCH_SELECTOR (selector));
3197 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3199 if (picker_result == GTK_RESPONSE_OK) {
3200 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3202 gtk_widget_destroy (picker_dialog);
3205 modest_address_book_add_address (selected);
3210 g_object_unref (selector);
3215 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}