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, dgettext("hildon-common-strings", "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, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1458 g_free (priv->last_search);
1459 priv->last_search = NULL;
1461 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1462 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1465 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1466 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1467 g_free (priv->last_search);
1468 priv->last_search = NULL;
1470 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1471 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1475 g_free (current_search);
1480 modest_msg_view_window_set_zoom (ModestWindow *window,
1483 ModestMsgViewWindowPrivate *priv;
1484 ModestWindowPrivate *parent_priv;
1486 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1489 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1490 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1495 modest_msg_view_window_get_zoom (ModestWindow *window)
1497 ModestMsgViewWindowPrivate *priv;
1499 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1501 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1502 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1506 modest_msg_view_window_zoom_plus (ModestWindow *window)
1509 ModestMsgViewWindowPrivate *priv;
1511 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1512 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1514 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1516 if (zoom_level >= 2.0) {
1517 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1519 } else if (zoom_level >= 1.5) {
1521 } else if (zoom_level >= 1.2) {
1523 } else if (zoom_level >= 1.0) {
1525 } else if (zoom_level >= 0.8) {
1527 } else if (zoom_level >= 0.5) {
1533 /* set zoom level */
1534 hildon_banner_show_information (NULL, NULL, _HL("wdgt_ib_zoom"));
1535 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1541 modest_msg_view_window_zoom_minus (ModestWindow *window)
1544 ModestMsgViewWindowPrivate *priv;
1546 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1547 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1549 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1551 if (zoom_level <= 0.5) {
1552 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1554 } else if (zoom_level <= 0.8) {
1556 } else if (zoom_level <= 1.0) {
1558 } else if (zoom_level <= 1.2) {
1560 } else if (zoom_level <= 1.5) {
1562 } else if (zoom_level <= 2.0) {
1568 /* set zoom level */
1569 hildon_banner_show_information (NULL, NULL, _HL("wdgt_ib_zoom"));
1570 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1577 modest_msg_view_window_key_event (GtkWidget *window,
1583 focus = gtk_window_get_focus (GTK_WINDOW (window));
1585 /* for the find toolbar case */
1586 if (focus && GTK_IS_ENTRY (focus)) {
1587 if (event->keyval == GDK_BackSpace) {
1589 copy = gdk_event_copy ((GdkEvent *) event);
1590 gtk_widget_event (focus, copy);
1591 gdk_event_free (copy);
1596 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1597 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1598 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1599 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1600 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1601 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1602 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1603 /* gboolean return_value; */
1605 if (event->type == GDK_KEY_PRESS) {
1606 GtkScrollType scroll_type;
1608 switch (event->keyval) {
1611 scroll_type = GTK_SCROLL_STEP_UP; break;
1614 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1616 case GDK_KP_Page_Up:
1617 scroll_type = GTK_SCROLL_PAGE_UP; break;
1619 case GDK_KP_Page_Down:
1620 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1623 scroll_type = GTK_SCROLL_START; break;
1626 scroll_type = GTK_SCROLL_END; break;
1627 default: scroll_type = GTK_SCROLL_NONE;
1630 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1631 /* scroll_type, FALSE, &return_value); */
1642 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1645 ModestMsgViewWindowPrivate *priv;
1646 GtkTreeIter tmp_iter;
1647 gboolean is_last_selected;
1649 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1650 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1652 /*if no model (so no rows at all), then virtually we are the last*/
1653 if (!priv->header_model || !priv->row_reference)
1656 if (!gtk_tree_row_reference_valid (priv->row_reference))
1659 path = gtk_tree_row_reference_get_path (priv->row_reference);
1663 is_last_selected = TRUE;
1664 while (is_last_selected) {
1666 gtk_tree_path_next (path);
1667 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1669 gtk_tree_model_get (priv->header_model, &tmp_iter,
1670 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1673 if (msg_is_visible (header, priv->is_outbox))
1674 is_last_selected = FALSE;
1675 g_object_unref(G_OBJECT(header));
1678 gtk_tree_path_free (path);
1679 return is_last_selected;
1683 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1685 ModestMsgViewWindowPrivate *priv;
1687 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1688 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1690 return priv->header_model != NULL;
1694 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1696 ModestMsgViewWindowPrivate *priv;
1698 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1699 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1701 return priv->is_search_result;
1705 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1707 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1709 if (!check_outbox) {
1712 ModestTnySendQueueStatus status;
1713 status = modest_tny_all_send_queues_get_msg_status (header);
1714 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1715 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1720 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1723 ModestMsgViewWindowPrivate *priv;
1724 gboolean is_first_selected;
1725 GtkTreeIter tmp_iter;
1727 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1728 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1730 /*if no model (so no rows at all), then virtually we are the first*/
1731 if (!priv->header_model || !priv->row_reference)
1734 if (!gtk_tree_row_reference_valid (priv->row_reference))
1737 path = gtk_tree_row_reference_get_path (priv->row_reference);
1741 is_first_selected = TRUE;
1742 while (is_first_selected) {
1744 if(!gtk_tree_path_prev (path))
1746 /* Here the 'if' is needless for logic, but let make sure
1747 * iter is valid for gtk_tree_model_get. */
1748 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1750 gtk_tree_model_get (priv->header_model, &tmp_iter,
1751 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1754 if (msg_is_visible (header, priv->is_outbox))
1755 is_first_selected = FALSE;
1756 g_object_unref(G_OBJECT(header));
1759 gtk_tree_path_free (path);
1760 return is_first_selected;
1765 GtkTreeRowReference *row_reference;
1769 message_reader_performer (gboolean canceled,
1771 GtkWindow *parent_window,
1772 TnyAccount *account,
1775 ModestMailOperation *mail_op = NULL;
1776 MsgReaderInfo *info;
1778 info = (MsgReaderInfo *) user_data;
1779 if (canceled || err) {
1783 /* Register the header - it'll be unregistered in the callback */
1784 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1786 /* New mail operation */
1787 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1788 modest_ui_actions_disk_operations_error_handler,
1791 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1792 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1793 g_object_unref (mail_op);
1795 /* Update dimming rules */
1796 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1797 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1800 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1801 g_object_unref (info->header);
1802 g_slice_free (MsgReaderInfo, info);
1807 * Reads the message whose summary item is @header. It takes care of
1808 * several things, among others:
1810 * If the message was not previously downloaded then ask the user
1811 * before downloading. If there is no connection launch the connection
1812 * dialog. Update toolbar dimming rules.
1814 * Returns: TRUE if the mail operation was started, otherwise if the
1815 * user do not want to download the message, or if the user do not
1816 * want to connect, then the operation is not issued
1819 message_reader (ModestMsgViewWindow *window,
1820 ModestMsgViewWindowPrivate *priv,
1822 GtkTreeRowReference *row_reference)
1824 ModestWindowMgr *mgr;
1825 TnyAccount *account;
1827 MsgReaderInfo *info;
1829 g_return_val_if_fail (row_reference != NULL, FALSE);
1831 mgr = modest_runtime_get_window_mgr ();
1832 /* Msg download completed */
1833 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1835 /* We set the header from model while we're loading */
1836 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1837 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1839 /* Ask the user if he wants to download the message if
1841 if (!tny_device_is_online (modest_runtime_get_device())) {
1842 GtkResponseType response;
1844 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1845 _("mcen_nc_get_msg"));
1846 if (response == GTK_RESPONSE_CANCEL)
1849 folder = tny_header_get_folder (header);
1850 info = g_slice_new (MsgReaderInfo);
1851 info->header = g_object_ref (header);
1852 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1854 /* Offer the connection dialog if necessary */
1855 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1857 TNY_FOLDER_STORE (folder),
1858 message_reader_performer,
1860 g_object_unref (folder);
1865 folder = tny_header_get_folder (header);
1866 account = tny_folder_get_account (folder);
1867 info = g_slice_new (MsgReaderInfo);
1868 info->header = g_object_ref (header);
1869 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1871 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1872 g_object_unref (account);
1873 g_object_unref (folder);
1879 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1881 ModestMsgViewWindowPrivate *priv;
1882 GtkTreePath *path= NULL;
1883 GtkTreeIter tmp_iter;
1885 gboolean retval = TRUE;
1886 GtkTreeRowReference *row_reference = NULL;
1888 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1889 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1891 if (!priv->row_reference)
1894 /* Update the next row reference if it's not valid. This could
1895 happen if for example the header which it was pointing to,
1896 was deleted. The best place to do it is in the row-deleted
1897 handler but the tinymail model do not work like the glib
1898 tree models and reports the deletion when the row is still
1900 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1901 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1902 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1903 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1906 if (priv->next_row_reference)
1907 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1911 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1913 gtk_tree_model_get_iter (priv->header_model,
1916 gtk_tree_path_free (path);
1918 gtk_tree_model_get (priv->header_model, &tmp_iter,
1919 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1922 /* Read the message & show it */
1923 if (!message_reader (window, priv, header, row_reference)) {
1926 gtk_tree_row_reference_free (row_reference);
1929 g_object_unref (header);
1935 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1937 ModestMsgViewWindowPrivate *priv = NULL;
1939 gboolean finished = FALSE;
1940 gboolean retval = FALSE;
1942 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1943 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1945 /* Return inmediatly if there is no header model */
1946 if (!priv->header_model || !priv->row_reference)
1949 path = gtk_tree_row_reference_get_path (priv->row_reference);
1950 while (!finished && gtk_tree_path_prev (path)) {
1954 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1955 gtk_tree_model_get (priv->header_model, &iter,
1956 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1960 if (msg_is_visible (header, priv->is_outbox)) {
1961 GtkTreeRowReference *row_reference;
1962 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1963 /* Read the message & show it */
1964 retval = message_reader (window, priv, header, row_reference);
1965 gtk_tree_row_reference_free (row_reference);
1969 g_object_unref (header);
1973 gtk_tree_path_free (path);
1978 view_msg_cb (ModestMailOperation *mail_op,
1985 ModestMsgViewWindow *self = NULL;
1986 ModestMsgViewWindowPrivate *priv = NULL;
1987 GtkTreeRowReference *row_reference = NULL;
1989 /* Unregister the header (it was registered before creating the mail operation) */
1990 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
1992 row_reference = (GtkTreeRowReference *) user_data;
1994 gtk_tree_row_reference_free (row_reference);
1998 /* If there was any error */
1999 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2000 gtk_tree_row_reference_free (row_reference);
2004 /* Get the window */
2005 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2006 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2007 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2009 /* Update the row reference */
2010 if (priv->row_reference != NULL) {
2011 gtk_tree_row_reference_free (priv->row_reference);
2012 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2013 if (priv->next_row_reference != NULL) {
2014 gtk_tree_row_reference_free (priv->next_row_reference);
2016 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2017 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2020 /* Mark header as read */
2021 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2022 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2024 /* Set new message */
2025 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2026 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2027 modest_msg_view_window_update_priority (self);
2028 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2029 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2032 /* Set the new message uid of the window */
2033 if (priv->msg_uid) {
2034 g_free (priv->msg_uid);
2035 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2038 /* Notify the observers */
2039 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2040 0, priv->header_model, priv->row_reference);
2043 g_object_unref (self);
2044 gtk_tree_row_reference_free (row_reference);
2048 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2050 ModestMsgViewWindowPrivate *priv;
2052 TnyFolderType folder_type;
2054 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2056 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2058 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2062 folder = tny_msg_get_folder (msg);
2064 folder_type = modest_tny_folder_guess_folder_type (folder);
2065 g_object_unref (folder);
2067 g_object_unref (msg);
2075 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2077 ModestMsgViewWindowPrivate *priv;
2078 TnyHeader *header = NULL;
2079 TnyHeaderFlags flags = 0;
2081 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2083 if (priv->header_model && priv->row_reference) {
2085 GtkTreePath *path = NULL;
2087 path = gtk_tree_row_reference_get_path (priv->row_reference);
2088 g_return_if_fail (path != NULL);
2089 gtk_tree_model_get_iter (priv->header_model,
2091 gtk_tree_row_reference_get_path (priv->row_reference));
2093 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2095 gtk_tree_path_free (path);
2098 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2100 header = tny_msg_get_header (msg);
2101 g_object_unref (msg);
2106 flags = tny_header_get_flags (header);
2107 g_object_unref(G_OBJECT(header));
2110 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2115 toolbar_resize (ModestMsgViewWindow *self)
2117 ModestMsgViewWindowPrivate *priv = NULL;
2118 ModestWindowPrivate *parent_priv = NULL;
2120 gint static_button_size;
2121 ModestWindowMgr *mgr;
2123 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2124 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2125 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2127 mgr = modest_runtime_get_window_mgr ();
2128 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2130 if (parent_priv->toolbar) {
2131 /* left size buttons */
2132 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2133 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2134 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2135 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2136 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2137 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2138 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2139 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2140 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2141 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2142 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2143 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2144 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2145 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2146 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2147 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2149 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2150 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2151 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2152 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2158 modest_msg_view_window_show_toolbar (ModestWindow *self,
2159 gboolean show_toolbar)
2161 ModestMsgViewWindowPrivate *priv = NULL;
2162 ModestWindowPrivate *parent_priv;
2163 GtkWidget *reply_button = NULL, *menu = NULL;
2165 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2166 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2168 /* Set optimized view status */
2169 priv->optimized_view = !show_toolbar;
2171 if (!parent_priv->toolbar) {
2172 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2174 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2176 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2177 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2178 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2181 hildon_window_add_toolbar (HILDON_WINDOW (self),
2182 GTK_TOOLBAR (parent_priv->toolbar));
2184 /* Set reply button tap and hold menu */
2185 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2186 "/ToolBar/ToolbarMessageReply");
2187 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2188 "/ToolbarReplyCSM");
2189 if (menu && reply_button)
2190 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2194 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2195 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2196 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2198 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2199 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2200 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2202 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2205 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2206 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2211 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2213 ModestMsgViewWindow *window)
2215 if (!GTK_WIDGET_VISIBLE (window))
2218 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2222 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2224 ModestMsgViewWindowPrivate *priv;
2226 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2227 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2229 return priv->progress_hint;
2233 observers_empty (ModestMsgViewWindow *self)
2236 ModestMsgViewWindowPrivate *priv;
2237 gboolean is_empty = TRUE;
2238 guint pending_ops = 0;
2240 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2241 tmp = priv->progress_widgets;
2243 /* Check all observers */
2244 while (tmp && is_empty) {
2245 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2246 is_empty = pending_ops == 0;
2248 tmp = g_slist_next(tmp);
2255 on_account_removed (TnyAccountStore *account_store,
2256 TnyAccount *account,
2259 /* Do nothing if it's a transport account, because we only
2260 show the messages of a store account */
2261 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2262 const gchar *parent_acc = NULL;
2263 const gchar *our_acc = NULL;
2265 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2266 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2268 /* Close this window if I'm showing a message of the removed account */
2269 if (strcmp (parent_acc, our_acc) == 0)
2270 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2275 on_mail_operation_started (ModestMailOperation *mail_op,
2278 ModestMsgViewWindow *self;
2279 ModestMailOperationTypeOperation op_type;
2281 ModestMsgViewWindowPrivate *priv;
2282 GObject *source = NULL;
2284 self = MODEST_MSG_VIEW_WINDOW (user_data);
2285 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2286 op_type = modest_mail_operation_get_type_operation (mail_op);
2287 tmp = priv->progress_widgets;
2288 source = modest_mail_operation_get_source(mail_op);
2289 if (G_OBJECT (self) == source) {
2290 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2291 set_toolbar_transfer_mode(self);
2293 modest_progress_object_add_operation (
2294 MODEST_PROGRESS_OBJECT (tmp->data),
2296 tmp = g_slist_next (tmp);
2300 g_object_unref (source);
2304 on_mail_operation_finished (ModestMailOperation *mail_op,
2307 ModestMsgViewWindow *self;
2308 ModestMailOperationTypeOperation op_type;
2310 ModestMsgViewWindowPrivate *priv;
2312 self = MODEST_MSG_VIEW_WINDOW (user_data);
2313 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2314 op_type = modest_mail_operation_get_type_operation (mail_op);
2315 tmp = priv->progress_widgets;
2317 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2319 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2321 tmp = g_slist_next (tmp);
2324 /* If no more operations are being observed, NORMAL mode is enabled again */
2325 if (observers_empty (self)) {
2326 set_progress_hint (self, FALSE);
2329 /* Update dimming rules. We have to do this right here
2330 and not in view_msg_cb because at that point the
2331 transfer mode is still enabled so the dimming rule
2332 won't let the user delete the message that has been
2333 readed for example */
2334 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2335 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2340 on_queue_changed (ModestMailOperationQueue *queue,
2341 ModestMailOperation *mail_op,
2342 ModestMailOperationQueueNotification type,
2343 ModestMsgViewWindow *self)
2345 ModestMsgViewWindowPrivate *priv;
2347 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2349 /* If this operations was created by another window, do nothing */
2350 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2353 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2354 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2356 "operation-started",
2357 G_CALLBACK (on_mail_operation_started),
2359 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2361 "operation-finished",
2362 G_CALLBACK (on_mail_operation_finished),
2364 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2365 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2367 "operation-started");
2368 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2370 "operation-finished");
2375 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2377 ModestMsgViewWindowPrivate *priv;
2378 TnyList *selected_attachments = NULL;
2380 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2381 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2383 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2385 return selected_attachments;
2391 guint banner_idle_id;
2392 } DecodeAsyncHelper;
2395 decode_async_banner_idle (gpointer user_data)
2397 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2399 helper->banner_idle_id = 0;
2400 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2406 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2412 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2414 if (helper->banner_idle_id > 0) {
2415 g_source_remove (helper->banner_idle_id);
2416 helper->banner_idle_id = 0;
2418 if (helper->banner) {
2419 gtk_widget_destroy (helper->banner);
2420 helper->banner = NULL;
2422 if (cancelled || err) {
2423 modest_platform_information_banner (NULL, NULL,
2424 _("mail_ib_file_operation_failed"));
2428 /* make the file read-only */
2429 g_chmod(helper->filepath, 0444);
2431 /* Activate the file */
2432 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2436 g_free (helper->filepath);
2437 g_slice_free (DecodeAsyncHelper, helper);
2441 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2442 TnyMimePart *mime_part)
2444 ModestMsgViewWindowPrivate *priv;
2445 const gchar *msg_uid;
2446 gchar *attachment_uid = NULL;
2447 gint attachment_index = 0;
2448 TnyList *attachments;
2450 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2451 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2452 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2454 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2455 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2456 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2457 g_object_unref (attachments);
2459 if (msg_uid && attachment_index >= 0) {
2460 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2463 if (mime_part == NULL) {
2464 gboolean error = FALSE;
2465 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2466 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2468 } else if (tny_list_get_length (selected_attachments) > 1) {
2469 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2473 iter = tny_list_create_iterator (selected_attachments);
2474 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2475 g_object_unref (iter);
2477 g_object_unref (selected_attachments);
2482 g_object_ref (mime_part);
2485 if (tny_mime_part_is_purged (mime_part)) {
2486 g_object_unref (mime_part);
2490 if (!modest_tny_mime_part_is_msg (mime_part)) {
2491 gchar *filepath = NULL;
2492 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2493 gboolean show_error_banner = FALSE;
2494 TnyFsStream *temp_stream = NULL;
2495 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2498 if (temp_stream != NULL) {
2499 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2500 helper->filepath = g_strdup (filepath);
2501 helper->banner = NULL;
2502 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2503 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2504 on_decode_to_stream_async_handler,
2507 g_object_unref (temp_stream);
2508 /* NOTE: files in the temporary area will be automatically
2509 * cleaned after some time if they are no longer in use */
2512 const gchar *content_type;
2513 /* the file may already exist but it isn't writable,
2514 * let's try to open it anyway */
2515 content_type = tny_mime_part_get_content_type (mime_part);
2516 modest_platform_activate_file (filepath, content_type);
2518 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2519 show_error_banner = TRUE;
2524 if (show_error_banner)
2525 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2527 /* message attachment */
2528 TnyHeader *header = NULL;
2529 ModestWindowMgr *mgr;
2530 ModestWindow *msg_win = NULL;
2533 header = tny_msg_get_header (TNY_MSG (mime_part));
2534 mgr = modest_runtime_get_window_mgr ();
2535 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2538 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2539 * thus, we don't do anything */
2540 g_warning ("window for is already being created");
2542 /* it's not found, so create a new window for it */
2543 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2544 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2546 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2547 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2548 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2549 modest_window_get_zoom (MODEST_WINDOW (window)));
2550 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2551 gtk_widget_show_all (GTK_WIDGET (msg_win));
2554 g_object_unref (mime_part);
2567 GnomeVFSResult result;
2570 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2571 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2572 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2573 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2576 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2580 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2581 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2582 g_free (pair->filename);
2583 g_object_unref (pair->part);
2584 g_slice_free (SaveMimePartPair, pair);
2586 g_list_free (info->pairs);
2589 gtk_widget_destroy (info->banner);
2590 g_slice_free (SaveMimePartInfo, info);
2595 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2597 if (info->pairs != NULL) {
2598 save_mime_part_to_file (info);
2600 /* This is a GDK lock because we are an idle callback and
2601 * hildon_banner_show_information is or does Gtk+ code */
2603 gdk_threads_enter (); /* CHECKED */
2604 save_mime_part_info_free (info, TRUE);
2605 if (info->result == GNOME_VFS_OK) {
2606 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2607 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2608 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2609 "cerm_device_memory_full"));
2611 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2613 gdk_threads_leave (); /* CHECKED */
2620 save_mime_part_to_file (SaveMimePartInfo *info)
2622 GnomeVFSHandle *handle;
2624 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2626 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2627 if (info->result == GNOME_VFS_OK) {
2628 GError *error = NULL;
2629 stream = tny_vfs_stream_new (handle);
2630 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2631 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2633 info->result = GNOME_VFS_ERROR_IO;
2635 g_object_unref (G_OBJECT (stream));
2636 g_object_unref (pair->part);
2637 g_slice_free (SaveMimePartPair, pair);
2638 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2640 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2641 save_mime_part_info_free (info, FALSE);
2644 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2649 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2651 gboolean is_ok = TRUE;
2652 gint replaced_files = 0;
2653 const GList *files = info->pairs;
2656 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2657 SaveMimePartPair *pair = iter->data;
2658 if (modest_utils_file_exists (pair->filename)) {
2662 if (replaced_files) {
2663 GtkWidget *confirm_overwrite_dialog;
2664 const gchar *message = (replaced_files == 1) ?
2665 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2666 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2667 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2670 gtk_widget_destroy (confirm_overwrite_dialog);
2674 save_mime_part_info_free (info, TRUE);
2676 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2677 _CS("sfil_ib_saving"));
2678 info->banner = banner;
2679 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2685 save_attachments_response (GtkDialog *dialog,
2689 TnyList *mime_parts;
2691 GList *files_to_save = NULL;
2693 mime_parts = TNY_LIST (user_data);
2695 if (arg1 != GTK_RESPONSE_OK)
2698 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2700 if (!modest_utils_folder_writable (chooser_uri)) {
2701 hildon_banner_show_information
2702 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2706 iter = tny_list_create_iterator (mime_parts);
2707 while (!tny_iterator_is_done (iter)) {
2708 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2710 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2711 !tny_mime_part_is_purged (mime_part) &&
2712 (tny_mime_part_get_filename (mime_part) != NULL)) {
2713 SaveMimePartPair *pair;
2715 pair = g_slice_new0 (SaveMimePartPair);
2717 if (tny_list_get_length (mime_parts) > 1) {
2719 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2720 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2723 pair->filename = g_strdup (chooser_uri);
2725 pair->part = mime_part;
2726 files_to_save = g_list_prepend (files_to_save, pair);
2728 tny_iterator_next (iter);
2730 g_object_unref (iter);
2732 g_free (chooser_uri);
2734 if (files_to_save != NULL) {
2735 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2736 info->pairs = files_to_save;
2737 info->result = TRUE;
2738 save_mime_parts_to_file_with_checks (info);
2742 /* Free and close the dialog */
2743 g_object_unref (mime_parts);
2744 gtk_widget_destroy (GTK_WIDGET (dialog));
2748 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2750 ModestMsgViewWindowPrivate *priv;
2751 GtkWidget *save_dialog = NULL;
2752 gchar *folder = NULL;
2753 gchar *filename = NULL;
2754 gchar *save_multiple_str = NULL;
2756 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2757 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2759 if (mime_parts == NULL) {
2760 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2761 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2764 g_object_ref (mime_parts);
2767 /* prepare dialog */
2768 if (tny_list_get_length (mime_parts) == 1) {
2770 /* only one attachment selected */
2771 iter = tny_list_create_iterator (mime_parts);
2772 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2773 g_object_unref (iter);
2774 if (!modest_tny_mime_part_is_msg (mime_part) &&
2775 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2776 !tny_mime_part_is_purged (mime_part)) {
2777 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2779 /* TODO: show any error? */
2780 g_warning ("Tried to save a non-file attachment");
2781 g_object_unref (mime_parts);
2784 g_object_unref (mime_part);
2786 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2787 tny_list_get_length (mime_parts));
2790 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2791 GTK_FILE_CHOOSER_ACTION_SAVE);
2794 folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2795 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2800 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2805 /* if multiple, set multiple string */
2806 if (save_multiple_str) {
2807 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2808 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2811 /* We must run this asynchronously, because the hildon dialog
2812 performs a gtk_dialog_run by itself which leads to gdk
2814 g_signal_connect (save_dialog, "response",
2815 G_CALLBACK (save_attachments_response), mime_parts);
2817 gtk_widget_show_all (save_dialog);
2821 show_remove_attachment_information (gpointer userdata)
2823 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2824 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2826 /* We're outside the main lock */
2827 gdk_threads_enter ();
2829 if (priv->remove_attachment_banner != NULL) {
2830 gtk_widget_destroy (priv->remove_attachment_banner);
2831 g_object_unref (priv->remove_attachment_banner);
2834 priv->remove_attachment_banner = g_object_ref (
2835 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2837 gdk_threads_leave ();
2843 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2845 ModestMsgViewWindowPrivate *priv;
2846 TnyList *mime_parts = NULL;
2847 gchar *confirmation_message;
2853 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2854 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2857 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2859 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2861 /* Remove already purged messages from mime parts list */
2862 iter = tny_list_create_iterator (mime_parts);
2863 while (!tny_iterator_is_done (iter)) {
2864 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2865 tny_iterator_next (iter);
2866 if (tny_mime_part_is_purged (part)) {
2867 tny_list_remove (mime_parts, (GObject *) part);
2869 g_object_unref (part);
2871 g_object_unref (iter);
2873 if (tny_list_get_length (mime_parts) == 0) {
2874 g_object_unref (mime_parts);
2878 n_attachments = tny_list_get_length (mime_parts);
2879 if (n_attachments == 1) {
2883 iter = tny_list_create_iterator (mime_parts);
2884 part = (TnyMimePart *) tny_iterator_get_current (iter);
2885 g_object_unref (iter);
2886 if (modest_tny_mime_part_is_msg (part)) {
2888 header = tny_msg_get_header (TNY_MSG (part));
2889 filename = tny_header_dup_subject (header);
2890 g_object_unref (header);
2891 if (filename == NULL)
2892 filename = g_strdup (_("mail_va_no_subject"));
2894 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2896 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2898 g_object_unref (part);
2900 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2901 "mcen_nc_purge_files_text",
2902 n_attachments), n_attachments);
2904 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2905 confirmation_message);
2906 g_free (confirmation_message);
2908 if (response != GTK_RESPONSE_OK) {
2909 g_object_unref (mime_parts);
2913 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2915 iter = tny_list_create_iterator (mime_parts);
2916 while (!tny_iterator_is_done (iter)) {
2919 part = (TnyMimePart *) tny_iterator_get_current (iter);
2920 tny_mime_part_set_purged (TNY_MIME_PART (part));
2921 g_object_unref (part);
2922 tny_iterator_next (iter);
2924 g_object_unref (iter);
2926 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2927 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2928 tny_msg_rewrite_cache (msg);
2929 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2930 g_object_unref (msg);
2932 g_object_unref (mime_parts);
2934 if (priv->purge_timeout > 0) {
2935 g_source_remove (priv->purge_timeout);
2936 priv->purge_timeout = 0;
2939 if (priv->remove_attachment_banner) {
2940 gtk_widget_destroy (priv->remove_attachment_banner);
2941 g_object_unref (priv->remove_attachment_banner);
2942 priv->remove_attachment_banner = NULL;
2950 update_window_title (ModestMsgViewWindow *window)
2952 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2954 TnyHeader *header = NULL;
2955 gchar *subject = NULL;
2957 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2960 header = tny_msg_get_header (msg);
2961 subject = tny_header_dup_subject (header);
2962 g_object_unref (header);
2963 g_object_unref (msg);
2966 if ((subject == NULL)||(subject[0] == '\0')) {
2968 subject = g_strdup (_("mail_va_no_subject"));
2971 gtk_window_set_title (GTK_WINDOW (window), subject);
2975 static void on_move_focus (GtkWidget *widget,
2976 GtkDirectionType direction,
2979 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
2983 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
2985 GnomeVFSResult result;
2986 GnomeVFSHandle *handle = NULL;
2987 GnomeVFSFileInfo *info = NULL;
2990 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
2991 if (result != GNOME_VFS_OK) {
2996 info = gnome_vfs_file_info_new ();
2997 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
2998 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
2999 /* We put a "safe" default size for going to cache */
3000 *expected_size = (300*1024);
3002 *expected_size = info->size;
3004 gnome_vfs_file_info_unref (info);
3006 stream = tny_vfs_stream_new (handle);
3015 TnyStream *output_stream;
3016 GtkWidget *msg_view;
3020 on_fetch_image_idle_refresh_view (gpointer userdata)
3023 FetchImageData *fidata = (FetchImageData *) userdata;
3024 g_message ("REFRESH VIEW");
3025 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3026 g_message ("QUEUING DRAW");
3027 gtk_widget_queue_draw (fidata->msg_view);
3029 g_object_unref (fidata->msg_view);
3030 g_slice_free (FetchImageData, fidata);
3035 on_fetch_image_thread (gpointer userdata)
3037 FetchImageData *fidata = (FetchImageData *) userdata;
3038 TnyStreamCache *cache;
3039 TnyStream *cache_stream;
3041 cache = modest_runtime_get_images_cache ();
3042 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3043 g_free (fidata->cache_id);
3044 g_free (fidata->uri);
3046 if (cache_stream != NULL) {
3047 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3048 tny_stream_close (cache_stream);
3049 g_object_unref (cache_stream);
3052 tny_stream_close (fidata->output_stream);
3053 g_object_unref (fidata->output_stream);
3056 gdk_threads_enter ();
3057 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3058 gdk_threads_leave ();
3064 on_fetch_image (ModestMsgView *msgview,
3067 ModestMsgViewWindow *window)
3069 const gchar *current_account;
3070 ModestMsgViewWindowPrivate *priv;
3071 FetchImageData *fidata;
3073 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3075 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3077 fidata = g_slice_new0 (FetchImageData);
3078 fidata->msg_view = g_object_ref (msgview);
3079 fidata->uri = g_strdup (uri);
3080 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3081 fidata->output_stream = g_object_ref (stream);
3083 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3084 g_object_unref (fidata->output_stream);
3085 g_free (fidata->cache_id);
3086 g_free (fidata->uri);
3087 g_object_unref (fidata->msg_view);
3088 g_slice_free (FetchImageData, fidata);
3089 tny_stream_close (stream);
3097 setup_menu (ModestMsgViewWindow *self)
3099 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3101 /* Settings menu buttons */
3102 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3103 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3104 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3105 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3106 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3107 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3109 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3110 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3111 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3112 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3113 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3114 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3116 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3117 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3118 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3119 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3120 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3121 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3123 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3124 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3125 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3126 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3127 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3128 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3132 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3134 ModestMsgViewWindowPrivate *priv;
3135 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3136 GSList *recipients = NULL;
3138 gboolean contacts_to_add = FALSE;
3140 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3144 header = modest_msg_view_window_get_header (self);
3147 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3148 g_object_unref (header);
3150 recipients = modest_tny_msg_get_all_recipients_list (msg);
3151 g_object_unref (msg);
3154 if (recipients != NULL) {
3155 GtkWidget *picker_dialog;
3156 GtkWidget *selector;
3158 gchar *selected = NULL;
3160 selector = hildon_touch_selector_new_text ();
3161 g_object_ref (selector);
3163 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3164 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3165 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3166 (const gchar *) node->data);
3167 contacts_to_add = TRUE;
3171 if (contacts_to_add) {
3174 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3175 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3177 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3178 HILDON_TOUCH_SELECTOR (selector));
3180 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3182 if (picker_result == GTK_RESPONSE_OK) {
3183 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3185 gtk_widget_destroy (picker_dialog);
3188 modest_address_book_add_address (selected);
3193 g_object_unref (selector);
3198 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}