1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
71 #include <X11/Xatom.h>
72 #include <X11/XKBlib.h>
73 #include <X11/Xdmcp.h>
75 #define MYDOCS_ENV "MYDOCSDIR"
76 #define DOCS_FOLDER ".documents"
78 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
79 struct _ModestMsgViewWindowPrivate {
82 GtkWidget *main_scroll;
83 GtkWidget *find_toolbar;
86 /* Progress observers */
87 GSList *progress_widgets;
90 GtkWidget *prev_toolitem;
91 GtkWidget *next_toolitem;
92 gboolean progress_hint;
94 /* Optimized view enabled */
95 gboolean optimized_view;
97 /* Whether this was created via the *_new_for_search_result() function. */
98 gboolean is_search_result;
100 /* Whether the message is in outbox */
103 /* A reference to the @model of the header view
104 * to allow selecting previous/next messages,
105 * if the message is currently selected in the header view.
107 const gchar *header_folder_id;
108 GtkTreeModel *header_model;
109 GtkTreeRowReference *row_reference;
110 GtkTreeRowReference *next_row_reference;
112 gulong clipboard_change_handler;
113 gulong queue_change_handler;
114 gulong account_removed_handler;
115 gulong row_changed_handler;
116 gulong row_deleted_handler;
117 gulong row_inserted_handler;
118 gulong rows_reordered_handler;
121 GtkWidget *remove_attachment_banner;
128 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
129 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
130 static void modest_header_view_observer_init(
131 ModestHeaderViewObserverIface *iface_class);
132 static void modest_msg_view_window_finalize (GObject *obj);
133 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
135 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
136 ModestMsgViewWindow *obj);
137 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
138 ModestMsgViewWindow *obj);
140 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
142 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
143 static void modest_msg_view_window_set_zoom (ModestWindow *window,
145 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
146 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
147 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
150 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
152 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
153 gboolean show_toolbar);
155 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
157 ModestMsgViewWindow *window);
159 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
162 ModestMsgViewWindow *window);
164 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
166 ModestMsgViewWindow *window);
168 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
169 GtkTreePath *tree_path,
170 GtkTreeIter *tree_iter,
171 ModestMsgViewWindow *window);
173 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
177 ModestMsgViewWindow *window);
179 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
181 const gchar *tny_folder_id);
183 static void on_queue_changed (ModestMailOperationQueue *queue,
184 ModestMailOperation *mail_op,
185 ModestMailOperationQueueNotification type,
186 ModestMsgViewWindow *self);
188 static void on_account_removed (TnyAccountStore *account_store,
192 static void on_move_focus (GtkWidget *widget,
193 GtkDirectionType direction,
196 static void view_msg_cb (ModestMailOperation *mail_op,
203 static void set_progress_hint (ModestMsgViewWindow *self,
206 static void update_window_title (ModestMsgViewWindow *window);
208 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
209 static void init_window (ModestMsgViewWindow *obj);
211 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
213 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
215 static gboolean on_fetch_image (ModestMsgView *msgview,
218 ModestMsgViewWindow *window);
220 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
221 GtkScrollType scroll_type,
224 static gboolean message_reader (ModestMsgViewWindow *window,
225 ModestMsgViewWindowPrivate *priv,
227 GtkTreeRowReference *row_reference);
229 static void setup_menu (ModestMsgViewWindow *self);
230 static gboolean _modest_msg_view_window_map_event (GtkWidget *widget,
233 static void _make_zoom_buttons_grabeable (GtkWidget* widget);
236 /* list my signals */
243 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
244 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
248 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
249 MODEST_TYPE_MSG_VIEW_WINDOW, \
250 ModestMsgViewWindowPrivate))
252 static GtkWindowClass *parent_class = NULL;
254 /* uncomment the following if you have defined any signals */
255 static guint signals[LAST_SIGNAL] = {0};
258 modest_msg_view_window_get_type (void)
260 static GType my_type = 0;
262 static const GTypeInfo my_info = {
263 sizeof(ModestMsgViewWindowClass),
264 NULL, /* base init */
265 NULL, /* base finalize */
266 (GClassInitFunc) modest_msg_view_window_class_init,
267 NULL, /* class finalize */
268 NULL, /* class data */
269 sizeof(ModestMsgViewWindow),
271 (GInstanceInitFunc) modest_msg_view_window_init,
274 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
275 "ModestMsgViewWindow",
278 static const GInterfaceInfo modest_header_view_observer_info =
280 (GInterfaceInitFunc) modest_header_view_observer_init,
281 NULL, /* interface_finalize */
282 NULL /* interface_data */
285 g_type_add_interface_static (my_type,
286 MODEST_TYPE_HEADER_VIEW_OBSERVER,
287 &modest_header_view_observer_info);
293 save_state (ModestWindow *self)
295 modest_widget_memory_save (modest_runtime_get_conf (),
297 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
301 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
302 GtkScrollType scroll_type,
306 ModestMsgViewWindowPrivate *priv;
307 gboolean return_value;
309 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
310 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
315 add_scroll_binding (GtkBindingSet *binding_set,
317 GtkScrollType scroll)
319 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
321 gtk_binding_entry_add_signal (binding_set, keyval, 0,
323 GTK_TYPE_SCROLL_TYPE, scroll,
324 G_TYPE_BOOLEAN, FALSE);
325 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
327 GTK_TYPE_SCROLL_TYPE, scroll,
328 G_TYPE_BOOLEAN, FALSE);
332 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
334 GObjectClass *gobject_class;
335 HildonWindowClass *hildon_window_class;
336 ModestWindowClass *modest_window_class;
337 GtkBindingSet *binding_set;
339 gobject_class = (GObjectClass*) klass;
340 hildon_window_class = (HildonWindowClass *) klass;
341 modest_window_class = (ModestWindowClass *) klass;
343 parent_class = g_type_class_peek_parent (klass);
344 gobject_class->finalize = modest_msg_view_window_finalize;
346 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
347 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
348 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
349 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
350 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
351 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
353 modest_window_class->save_state_func = save_state;
355 klass->scroll_child = modest_msg_view_window_scroll_child;
357 signals[MSG_CHANGED_SIGNAL] =
358 g_signal_new ("msg-changed",
359 G_TYPE_FROM_CLASS (gobject_class),
361 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
363 modest_marshal_VOID__POINTER_POINTER,
364 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
366 signals[SCROLL_CHILD_SIGNAL] =
367 g_signal_new ("scroll-child",
368 G_TYPE_FROM_CLASS (gobject_class),
369 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
370 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
372 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
373 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
375 binding_set = gtk_binding_set_by_class (klass);
376 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
377 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
378 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
379 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
380 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
381 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
383 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
387 static void modest_header_view_observer_init(
388 ModestHeaderViewObserverIface *iface_class)
390 iface_class->update_func = modest_msg_view_window_update_model_replaced;
394 modest_msg_view_window_init (ModestMsgViewWindow *obj)
396 ModestMsgViewWindowPrivate *priv;
397 ModestWindowPrivate *parent_priv = NULL;
398 GtkActionGroup *action_group = NULL;
399 GError *error = NULL;
400 GdkPixbuf *window_icon;
402 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
403 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
404 parent_priv->ui_manager = gtk_ui_manager_new();
406 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
407 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
409 /* Add common actions */
410 gtk_action_group_add_actions (action_group,
411 modest_action_entries,
412 G_N_ELEMENTS (modest_action_entries),
414 gtk_action_group_add_toggle_actions (action_group,
415 msg_view_toggle_action_entries,
416 G_N_ELEMENTS (msg_view_toggle_action_entries),
419 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
420 g_object_unref (action_group);
422 /* Load the UI definition */
423 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
426 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
427 g_error_free (error);
432 /* Add accelerators */
433 gtk_window_add_accel_group (GTK_WINDOW (obj),
434 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
436 priv->is_search_result = FALSE;
437 priv->is_outbox = FALSE;
439 priv->msg_view = NULL;
440 priv->header_model = NULL;
441 priv->header_folder_id = NULL;
442 priv->clipboard_change_handler = 0;
443 priv->queue_change_handler = 0;
444 priv->account_removed_handler = 0;
445 priv->row_changed_handler = 0;
446 priv->row_deleted_handler = 0;
447 priv->row_inserted_handler = 0;
448 priv->rows_reordered_handler = 0;
449 priv->progress_hint = FALSE;
451 priv->optimized_view = FALSE;
452 priv->purge_timeout = 0;
453 priv->remove_attachment_banner = NULL;
454 priv->msg_uid = NULL;
456 priv->sighandlers = NULL;
459 init_window (MODEST_MSG_VIEW_WINDOW(obj));
461 /* Set window icon */
462 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
464 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
465 g_object_unref (window_icon);
468 hildon_program_add_window (hildon_program_get_instance(),
475 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
477 ModestMsgViewWindowPrivate *priv = NULL;
479 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
481 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
483 set_progress_hint (self, TRUE);
489 set_progress_hint (ModestMsgViewWindow *self,
492 ModestWindowPrivate *parent_priv;
493 ModestMsgViewWindowPrivate *priv;
495 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
497 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
498 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
500 /* Sets current progress hint */
501 priv->progress_hint = enabled;
503 if (GTK_WIDGET_VISIBLE (self)) {
504 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
511 init_window (ModestMsgViewWindow *obj)
513 GtkWidget *main_vbox;
514 ModestMsgViewWindowPrivate *priv;
516 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
518 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
519 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
520 main_vbox = gtk_vbox_new (FALSE, 6);
521 #ifdef MODEST_TOOLKIT_HILDON2
522 priv->main_scroll = hildon_pannable_area_new ();
523 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
525 #ifdef MODEST_USE_MOZEMBED
526 priv->main_scroll = priv->msg_view;
527 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
529 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
530 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
532 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
533 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
534 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
537 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
538 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
540 priv->find_toolbar = hildon_find_toolbar_new (NULL);
541 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
542 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
544 /* NULL-ize fields if the window is destroyed */
545 g_signal_connect (priv->msg_view, "destroy", G_CALLBACK (gtk_widget_destroyed), &(priv->msg_view));
547 gtk_widget_show_all (GTK_WIDGET(main_vbox));
551 modest_msg_view_window_disconnect_signals (ModestWindow *self)
553 ModestMsgViewWindowPrivate *priv;
554 GtkWidget *header_view = NULL;
555 GtkWindow *parent_window = NULL;
557 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
559 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
560 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
561 priv->clipboard_change_handler))
562 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
563 priv->clipboard_change_handler);
565 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
566 priv->queue_change_handler))
567 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
568 priv->queue_change_handler);
570 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
571 priv->account_removed_handler))
572 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
573 priv->account_removed_handler);
575 if (priv->header_model) {
576 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
577 priv->row_changed_handler))
578 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
579 priv->row_changed_handler);
581 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
582 priv->row_deleted_handler))
583 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
584 priv->row_deleted_handler);
586 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
587 priv->row_inserted_handler))
588 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
589 priv->row_inserted_handler);
591 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
592 priv->rows_reordered_handler))
593 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
594 priv->rows_reordered_handler);
597 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
598 priv->sighandlers = NULL;
600 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
601 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
602 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
604 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
605 MODEST_HEADER_VIEW_OBSERVER(self));
611 modest_msg_view_window_finalize (GObject *obj)
613 ModestMsgViewWindowPrivate *priv;
615 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
617 /* Sanity check: shouldn't be needed, the window mgr should
618 call this function before */
619 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
621 if (priv->header_model != NULL) {
622 g_object_unref (priv->header_model);
623 priv->header_model = NULL;
626 if (priv->remove_attachment_banner) {
627 gtk_widget_destroy (priv->remove_attachment_banner);
628 g_object_unref (priv->remove_attachment_banner);
629 priv->remove_attachment_banner = NULL;
632 if (priv->purge_timeout > 0) {
633 g_source_remove (priv->purge_timeout);
634 priv->purge_timeout = 0;
637 if (priv->row_reference) {
638 gtk_tree_row_reference_free (priv->row_reference);
639 priv->row_reference = NULL;
642 if (priv->next_row_reference) {
643 gtk_tree_row_reference_free (priv->next_row_reference);
644 priv->next_row_reference = NULL;
648 g_free (priv->msg_uid);
649 priv->msg_uid = NULL;
652 G_OBJECT_CLASS(parent_class)->finalize (obj);
656 select_next_valid_row (GtkTreeModel *model,
657 GtkTreeRowReference **row_reference,
661 GtkTreeIter tmp_iter;
663 GtkTreePath *next = NULL;
664 gboolean retval = FALSE, finished;
666 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
668 path = gtk_tree_row_reference_get_path (*row_reference);
669 gtk_tree_model_get_iter (model, &tmp_iter, path);
670 gtk_tree_row_reference_free (*row_reference);
671 *row_reference = NULL;
675 TnyHeader *header = NULL;
677 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
678 gtk_tree_model_get (model, &tmp_iter,
679 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
683 if (msg_is_visible (header, is_outbox)) {
684 next = gtk_tree_model_get_path (model, &tmp_iter);
685 *row_reference = gtk_tree_row_reference_new (model, next);
686 gtk_tree_path_free (next);
690 g_object_unref (header);
693 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
694 next = gtk_tree_model_get_path (model, &tmp_iter);
696 /* Ensure that we are not selecting the same */
697 if (gtk_tree_path_compare (path, next) != 0) {
698 gtk_tree_model_get (model, &tmp_iter,
699 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
702 if (msg_is_visible (header, is_outbox)) {
703 *row_reference = gtk_tree_row_reference_new (model, next);
707 g_object_unref (header);
711 /* If we ended up in the same message
712 then there is no valid next
716 gtk_tree_path_free (next);
718 /* If there are no more messages and we don't
719 want to start again in the first one then
720 there is no valid next message */
726 gtk_tree_path_free (path);
731 /* TODO: This should be in _init(), with the parameters as properties. */
733 modest_msg_view_window_construct (ModestMsgViewWindow *self,
734 const gchar *modest_account_name,
735 const gchar *mailbox,
736 const gchar *msg_uid)
739 ModestMsgViewWindowPrivate *priv = NULL;
740 ModestWindowPrivate *parent_priv = NULL;
741 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
742 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
744 obj = G_OBJECT (self);
745 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
746 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
748 priv->msg_uid = g_strdup (msg_uid);
751 parent_priv->menubar = NULL;
753 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
754 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
757 /* Add common dimming rules */
758 modest_dimming_rules_group_add_rules (toolbar_rules_group,
759 modest_msg_view_toolbar_dimming_entries,
760 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
761 MODEST_WINDOW (self));
762 modest_dimming_rules_group_add_rules (clipboard_rules_group,
763 modest_msg_view_clipboard_dimming_entries,
764 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
765 MODEST_WINDOW (self));
767 /* Insert dimming rules group for this window */
768 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
769 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
770 g_object_unref (toolbar_rules_group);
771 g_object_unref (clipboard_rules_group);
773 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
775 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);
776 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
777 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
778 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
779 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
780 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
781 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
782 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
783 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
784 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
785 G_CALLBACK (modest_ui_actions_on_details), obj);
786 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
787 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
788 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
789 G_CALLBACK (on_fetch_image), obj);
791 g_signal_connect (G_OBJECT (obj), "key-release-event",
792 G_CALLBACK (modest_msg_view_window_key_event),
795 g_signal_connect (G_OBJECT (obj), "key-press-event",
796 G_CALLBACK (modest_msg_view_window_key_event),
799 g_signal_connect (G_OBJECT (obj), "move-focus",
800 G_CALLBACK (on_move_focus), obj);
802 g_signal_connect (G_OBJECT (obj), "map-event",
803 G_CALLBACK (_modest_msg_view_window_map_event),
806 /* Mail Operation Queue */
807 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
809 G_CALLBACK (on_queue_changed),
812 /* Account manager */
813 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
815 G_CALLBACK(on_account_removed),
818 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
819 modest_window_set_active_mailbox (MODEST_WINDOW(obj), mailbox);
821 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
822 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
823 priv->last_search = NULL;
825 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
827 /* Init the clipboard actions dim status */
828 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
830 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
835 /* FIXME: parameter checks */
837 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
838 const gchar *modest_account_name,
839 const gchar *mailbox,
840 const gchar *msg_uid,
842 GtkTreeRowReference *row_reference)
844 ModestMsgViewWindow *window = NULL;
845 ModestMsgViewWindowPrivate *priv = NULL;
846 TnyFolder *header_folder = NULL;
847 ModestHeaderView *header_view = NULL;
848 ModestWindow *main_window = NULL;
849 ModestWindowMgr *mgr = NULL;
852 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
855 mgr = modest_runtime_get_window_mgr ();
856 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
857 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
859 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
861 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
863 /* Remember the message list's TreeModel so we can detect changes
864 * and change the list selection when necessary: */
866 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
868 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
869 MODEST_MAIN_WINDOW(main_window),
870 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
873 if (header_view != NULL){
874 header_folder = modest_header_view_get_folder(header_view);
875 /* This could happen if the header folder was
876 unseleted before opening this msg window (for
877 example if the user selects an account in the
878 folder view of the main window */
880 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
881 priv->header_folder_id = tny_folder_get_id(header_folder);
882 g_assert(priv->header_folder_id != NULL);
883 g_object_unref(header_folder);
887 /* Setup row references and connect signals */
888 priv->header_model = g_object_ref (model);
891 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
892 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
893 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
895 priv->row_reference = NULL;
896 priv->next_row_reference = NULL;
899 /* Connect signals */
900 priv->row_changed_handler =
901 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
902 G_CALLBACK(modest_msg_view_window_on_row_changed),
904 priv->row_deleted_handler =
905 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
906 G_CALLBACK(modest_msg_view_window_on_row_deleted),
908 priv->row_inserted_handler =
909 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
910 G_CALLBACK(modest_msg_view_window_on_row_inserted),
912 priv->rows_reordered_handler =
913 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
914 G_CALLBACK(modest_msg_view_window_on_row_reordered),
917 if (header_view != NULL){
918 modest_header_view_add_observer(header_view,
919 MODEST_HEADER_VIEW_OBSERVER(window));
922 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
923 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
925 /* gtk_widget_show_all (GTK_WIDGET (window)); */
926 modest_msg_view_window_update_priority (window);
927 /* Check dimming rules */
928 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
929 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
930 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
932 return MODEST_WINDOW(window);
936 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
937 const gchar *modest_account_name,
938 const gchar *mailbox,
939 const gchar *msg_uid,
940 GtkTreeRowReference *row_reference)
942 ModestMsgViewWindow *window = NULL;
943 ModestMsgViewWindowPrivate *priv = NULL;
944 TnyFolder *header_folder = NULL;
945 ModestWindowMgr *mgr = NULL;
949 mgr = modest_runtime_get_window_mgr ();
950 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
951 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
953 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
955 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
957 /* Remember the message list's TreeModel so we can detect changes
958 * and change the list selection when necessary: */
960 if (header_view != NULL){
961 header_folder = modest_header_view_get_folder(header_view);
962 /* This could happen if the header folder was
963 unseleted before opening this msg window (for
964 example if the user selects an account in the
965 folder view of the main window */
967 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
968 priv->header_folder_id = tny_folder_get_id(header_folder);
969 g_assert(priv->header_folder_id != NULL);
970 g_object_unref(header_folder);
974 /* Setup row references and connect signals */
975 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
976 g_object_ref (priv->header_model);
979 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
980 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
981 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
983 priv->row_reference = NULL;
984 priv->next_row_reference = NULL;
987 /* Connect signals */
988 priv->row_changed_handler =
989 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
990 G_CALLBACK(modest_msg_view_window_on_row_changed),
992 priv->row_deleted_handler =
993 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
994 G_CALLBACK(modest_msg_view_window_on_row_deleted),
996 priv->row_inserted_handler =
997 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
998 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1000 priv->rows_reordered_handler =
1001 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
1002 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1005 if (header_view != NULL){
1006 modest_header_view_add_observer(header_view,
1007 MODEST_HEADER_VIEW_OBSERVER(window));
1010 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
1012 path = gtk_tree_row_reference_get_path (row_reference);
1013 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
1015 gtk_tree_model_get (priv->header_model, &iter,
1016 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1018 message_reader (window, priv, header, row_reference);
1020 gtk_tree_path_free (path);
1022 /* Check dimming rules */
1023 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1024 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1025 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1027 return MODEST_WINDOW(window);
1031 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1032 const gchar *modest_account_name,
1033 const gchar *mailbox,
1034 const gchar *msg_uid)
1036 ModestMsgViewWindow *window = NULL;
1037 ModestMsgViewWindowPrivate *priv = NULL;
1038 ModestWindowMgr *mgr = NULL;
1040 mgr = modest_runtime_get_window_mgr ();
1041 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1042 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1043 modest_msg_view_window_construct (window, modest_account_name, mailbox, msg_uid);
1045 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1047 /* Remember that this is a search result,
1048 * so we can disable some UI appropriately: */
1049 priv->is_search_result = TRUE;
1051 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1053 update_window_title (window);
1054 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1055 modest_msg_view_window_update_priority (window);
1057 /* Check dimming rules */
1058 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1059 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1060 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1062 return MODEST_WINDOW(window);
1066 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1067 const gchar *modest_account_name,
1068 const gchar *mailbox,
1069 const gchar *msg_uid)
1071 GObject *obj = NULL;
1072 ModestMsgViewWindowPrivate *priv;
1073 ModestWindowMgr *mgr = NULL;
1075 g_return_val_if_fail (msg, NULL);
1076 mgr = modest_runtime_get_window_mgr ();
1077 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1078 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1079 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1080 modest_account_name, mailbox, msg_uid);
1082 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1083 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1085 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1087 /* Check dimming rules */
1088 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1089 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1090 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1092 return MODEST_WINDOW(obj);
1096 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1099 ModestMsgViewWindow *window)
1101 check_dimming_rules_after_change (window);
1105 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1107 ModestMsgViewWindow *window)
1109 check_dimming_rules_after_change (window);
1111 /* The window could have dissapeared */
1114 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1116 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1117 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1121 /* On insertions we check if the folder still has the message we are
1122 * showing or do not. If do not, we do nothing. Which means we are still
1123 * not attached to any header folder and thus next/prev buttons are
1124 * still dimmed. Once the message that is shown by msg-view is found, the
1125 * new model of header-view will be attached and the references will be set.
1126 * On each further insertions dimming rules will be checked. However
1127 * this requires extra CPU time at least works.
1128 * (An message might be deleted from TnyFolder and thus will not be
1129 * inserted into the model again for example if it is removed by the
1130 * imap server and the header view is refreshed.)
1133 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1134 GtkTreePath *tree_path,
1135 GtkTreeIter *tree_iter,
1136 ModestMsgViewWindow *window)
1138 ModestMsgViewWindowPrivate *priv = NULL;
1139 TnyHeader *header = NULL;
1141 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1142 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1144 g_assert (model == priv->header_model);
1146 /* Check if the newly inserted message is the same we are actually
1147 * showing. IF not, we should remain detached from the header model
1148 * and thus prev and next toolbar buttons should remain dimmed. */
1149 gtk_tree_model_get (model, tree_iter,
1150 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1153 if (TNY_IS_HEADER (header)) {
1156 uid = modest_tny_folder_get_header_unique_id (header);
1157 if (!g_str_equal(priv->msg_uid, uid)) {
1158 check_dimming_rules_after_change (window);
1160 g_object_unref (G_OBJECT(header));
1164 g_object_unref(G_OBJECT(header));
1167 if (priv->row_reference) {
1168 gtk_tree_row_reference_free (priv->row_reference);
1171 /* Setup row_reference for the actual msg. */
1172 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1173 if (priv->row_reference == NULL) {
1174 g_warning("No reference for msg header item.");
1178 /* Now set up next_row_reference. */
1179 if (priv->next_row_reference) {
1180 gtk_tree_row_reference_free (priv->next_row_reference);
1183 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1184 select_next_valid_row (priv->header_model,
1185 &(priv->next_row_reference), FALSE, priv->is_outbox);
1187 /* Connect the remaining callbacks to become able to detect
1188 * changes in header-view. */
1189 priv->row_changed_handler =
1190 g_signal_connect (priv->header_model, "row-changed",
1191 G_CALLBACK (modest_msg_view_window_on_row_changed),
1193 priv->row_deleted_handler =
1194 g_signal_connect (priv->header_model, "row-deleted",
1195 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1197 priv->rows_reordered_handler =
1198 g_signal_connect (priv->header_model, "rows-reordered",
1199 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1202 check_dimming_rules_after_change (window);
1206 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1210 ModestMsgViewWindow *window)
1212 ModestMsgViewWindowPrivate *priv = NULL;
1213 gboolean already_changed = FALSE;
1215 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1217 /* If the current row was reordered select the proper next
1218 valid row. The same if the next row reference changes */
1219 if (priv->row_reference &&
1220 gtk_tree_row_reference_valid (priv->row_reference)) {
1222 path = gtk_tree_row_reference_get_path (priv->row_reference);
1223 if (gtk_tree_path_compare (path, arg1) == 0) {
1224 if (priv->next_row_reference) {
1225 gtk_tree_row_reference_free (priv->next_row_reference);
1227 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1228 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1229 already_changed = TRUE;
1231 gtk_tree_path_free (path);
1233 if (!already_changed &&
1234 priv->next_row_reference &&
1235 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1237 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1238 if (gtk_tree_path_compare (path, arg1) == 0) {
1239 if (priv->next_row_reference) {
1240 gtk_tree_row_reference_free (priv->next_row_reference);
1242 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1243 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1245 gtk_tree_path_free (path);
1247 check_dimming_rules_after_change (window);
1250 /* The modest_msg_view_window_update_model_replaced implements update
1251 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1252 * actually belongs to the header-view is the same as the TnyFolder of
1253 * the message of msg-view or not. If they are different, there is
1254 * nothing to do. If they are the same, then the model has replaced and
1255 * the reference in msg-view shall be replaced from the old model to
1256 * the new model. In this case the view will be detached from it's
1257 * header folder. From this point the next/prev buttons are dimmed.
1260 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1261 GtkTreeModel *model,
1262 const gchar *tny_folder_id)
1264 ModestMsgViewWindowPrivate *priv = NULL;
1265 ModestMsgViewWindow *window = NULL;
1267 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1268 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1270 window = MODEST_MSG_VIEW_WINDOW(observer);
1271 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1273 /* If there is an other folder in the header-view then we do
1274 * not care about it's model (msg list). Else if the
1275 * header-view shows the folder the msg shown by us is in, we
1276 * shall replace our model reference and make some check. */
1277 if(model == NULL || tny_folder_id == NULL ||
1278 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1281 /* Model is changed(replaced), so we should forget the old
1282 * one. Because there might be other references and there
1283 * might be some change on the model even if we unreferenced
1284 * it, we need to disconnect our signals here. */
1285 if (priv->header_model) {
1286 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1287 priv->row_changed_handler))
1288 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1289 priv->row_changed_handler);
1290 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1291 priv->row_deleted_handler))
1292 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1293 priv->row_deleted_handler);
1294 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1295 priv->row_inserted_handler))
1296 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1297 priv->row_inserted_handler);
1298 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1299 priv->rows_reordered_handler))
1300 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1301 priv->rows_reordered_handler);
1304 if (priv->row_reference)
1305 gtk_tree_row_reference_free (priv->row_reference);
1306 if (priv->next_row_reference)
1307 gtk_tree_row_reference_free (priv->next_row_reference);
1308 g_object_unref(priv->header_model);
1311 priv->row_changed_handler = 0;
1312 priv->row_deleted_handler = 0;
1313 priv->row_inserted_handler = 0;
1314 priv->rows_reordered_handler = 0;
1315 priv->next_row_reference = NULL;
1316 priv->row_reference = NULL;
1317 priv->header_model = NULL;
1320 priv->header_model = g_object_ref (model);
1322 /* Also we must connect to the new model for row insertions.
1323 * Only for insertions now. We will need other ones only after
1324 * the msg is show by msg-view is added to the new model. */
1325 priv->row_inserted_handler =
1326 g_signal_connect (priv->header_model, "row-inserted",
1327 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1330 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1331 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1335 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1337 ModestMsgViewWindowPrivate *priv= NULL;
1339 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1340 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1342 return priv->progress_hint;
1346 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1348 ModestMsgViewWindowPrivate *priv= NULL;
1350 TnyHeader *header = NULL;
1351 GtkTreePath *path = NULL;
1354 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1355 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1357 /* If the message was not obtained from a treemodel,
1358 * for instance if it was opened directly by the search UI:
1360 if (priv->header_model == NULL ||
1361 priv->row_reference == NULL ||
1362 !gtk_tree_row_reference_valid (priv->row_reference)) {
1363 msg = modest_msg_view_window_get_message (self);
1365 header = tny_msg_get_header (msg);
1366 g_object_unref (msg);
1371 /* Get iter of the currently selected message in the header view: */
1372 path = gtk_tree_row_reference_get_path (priv->row_reference);
1373 g_return_val_if_fail (path != NULL, NULL);
1374 gtk_tree_model_get_iter (priv->header_model,
1378 /* Get current message header */
1379 gtk_tree_model_get (priv->header_model, &iter,
1380 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1383 gtk_tree_path_free (path);
1388 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1390 ModestMsgViewWindowPrivate *priv;
1392 g_return_val_if_fail (self, NULL);
1394 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1396 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1400 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1402 ModestMsgViewWindowPrivate *priv;
1404 g_return_val_if_fail (self, NULL);
1406 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1408 return (const gchar*) priv->msg_uid;
1412 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1415 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1416 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1417 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1421 is_active = gtk_toggle_action_get_active (toggle);
1424 gtk_widget_show (priv->find_toolbar);
1425 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1427 gtk_widget_hide (priv->find_toolbar);
1428 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1431 /* update the toggle buttons status */
1432 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1434 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1439 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1440 ModestMsgViewWindow *obj)
1442 GtkToggleAction *toggle;
1443 ModestWindowPrivate *parent_priv;
1444 ModestMsgViewWindowPrivate *priv;
1446 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1447 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1449 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1450 gtk_toggle_action_set_active (toggle, FALSE);
1451 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1455 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1456 ModestMsgViewWindow *obj)
1458 gchar *current_search;
1459 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1461 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1462 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1466 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1468 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1469 g_free (current_search);
1470 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1474 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1476 g_free (priv->last_search);
1477 priv->last_search = g_strdup (current_search);
1478 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1481 hildon_banner_show_information (NULL, NULL,
1482 _HL("ckct_ib_find_no_matches"));
1483 g_free (priv->last_search);
1484 priv->last_search = NULL;
1486 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1489 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1490 hildon_banner_show_information (NULL, NULL,
1491 _HL("ckct_ib_find_search_complete"));
1492 g_free (priv->last_search);
1493 priv->last_search = NULL;
1495 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1499 g_free (current_search);
1504 modest_msg_view_window_set_zoom (ModestWindow *window,
1507 ModestMsgViewWindowPrivate *priv;
1508 ModestWindowPrivate *parent_priv;
1510 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1512 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1513 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1514 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1519 modest_msg_view_window_get_zoom (ModestWindow *window)
1521 ModestMsgViewWindowPrivate *priv;
1523 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1526 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1530 modest_msg_view_window_zoom_plus (ModestWindow *window)
1533 ModestMsgViewWindowPrivate *priv;
1537 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1538 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1540 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1542 if (zoom_level >= 2.0) {
1543 hildon_banner_show_information (NULL, NULL,
1544 _CS("ckct_ib_max_zoom_level_reached"));
1546 } else if (zoom_level >= 1.5) {
1548 } else if (zoom_level >= 1.2) {
1550 } else if (zoom_level >= 1.0) {
1552 } else if (zoom_level >= 0.8) {
1554 } else if (zoom_level >= 0.5) {
1560 /* set zoom level */
1561 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1562 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1563 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1564 g_free (banner_text);
1565 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1571 modest_msg_view_window_zoom_minus (ModestWindow *window)
1574 ModestMsgViewWindowPrivate *priv;
1578 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1579 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1581 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1583 if (zoom_level <= 0.5) {
1584 hildon_banner_show_information (NULL, NULL,
1585 _CS("ckct_ib_min_zoom_level_reached"));
1587 } else if (zoom_level <= 0.8) {
1589 } else if (zoom_level <= 1.0) {
1591 } else if (zoom_level <= 1.2) {
1593 } else if (zoom_level <= 1.5) {
1595 } else if (zoom_level <= 2.0) {
1601 /* set zoom level */
1602 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1603 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1604 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1605 g_free (banner_text);
1606 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1613 modest_msg_view_window_key_event (GtkWidget *window,
1619 focus = gtk_window_get_focus (GTK_WINDOW (window));
1621 if (event->keyval == HILDON_HARDKEY_DECREASE && event->type == GDK_KEY_PRESS) {
1622 modest_ui_actions_on_zoom_minus (NULL, MODEST_WINDOW (window));
1625 if (event->keyval == HILDON_HARDKEY_INCREASE && event->type == GDK_KEY_PRESS) {
1626 modest_ui_actions_on_zoom_plus (NULL, MODEST_WINDOW (window));
1630 /* for the find toolbar case */
1631 if (focus && GTK_IS_ENTRY (focus)) {
1632 if (event->keyval == GDK_BackSpace) {
1634 copy = gdk_event_copy ((GdkEvent *) event);
1635 gtk_widget_event (focus, copy);
1636 gdk_event_free (copy);
1641 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1642 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1643 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1644 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1645 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1646 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1647 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1648 /* gboolean return_value; */
1650 if (event->type == GDK_KEY_PRESS) {
1651 GtkScrollType scroll_type;
1653 switch (event->keyval) {
1656 scroll_type = GTK_SCROLL_STEP_UP; break;
1659 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1661 case GDK_KP_Page_Up:
1662 scroll_type = GTK_SCROLL_PAGE_UP; break;
1664 case GDK_KP_Page_Down:
1665 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1668 scroll_type = GTK_SCROLL_START; break;
1671 scroll_type = GTK_SCROLL_END; break;
1672 default: scroll_type = GTK_SCROLL_NONE;
1675 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1676 /* scroll_type, FALSE, &return_value); */
1687 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1690 ModestMsgViewWindowPrivate *priv;
1691 GtkTreeIter tmp_iter;
1692 gboolean is_last_selected;
1694 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1695 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1697 /*if no model (so no rows at all), then virtually we are the last*/
1698 if (!priv->header_model || !priv->row_reference)
1701 if (!gtk_tree_row_reference_valid (priv->row_reference))
1704 path = gtk_tree_row_reference_get_path (priv->row_reference);
1708 is_last_selected = TRUE;
1709 while (is_last_selected) {
1711 gtk_tree_path_next (path);
1712 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1714 gtk_tree_model_get (priv->header_model, &tmp_iter,
1715 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1718 if (msg_is_visible (header, priv->is_outbox))
1719 is_last_selected = FALSE;
1720 g_object_unref(G_OBJECT(header));
1723 gtk_tree_path_free (path);
1724 return is_last_selected;
1728 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1730 ModestMsgViewWindowPrivate *priv;
1732 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1733 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1735 return priv->header_model != NULL;
1739 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1741 ModestMsgViewWindowPrivate *priv;
1743 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1744 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1746 return priv->is_search_result;
1750 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1752 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1754 if (!check_outbox) {
1757 ModestTnySendQueueStatus status;
1758 status = modest_tny_all_send_queues_get_msg_status (header);
1759 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1760 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1765 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1768 ModestMsgViewWindowPrivate *priv;
1769 gboolean is_first_selected;
1770 GtkTreeIter tmp_iter;
1772 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1773 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1775 /*if no model (so no rows at all), then virtually we are the first*/
1776 if (!priv->header_model || !priv->row_reference)
1779 if (!gtk_tree_row_reference_valid (priv->row_reference))
1782 path = gtk_tree_row_reference_get_path (priv->row_reference);
1786 is_first_selected = TRUE;
1787 while (is_first_selected) {
1789 if(!gtk_tree_path_prev (path))
1791 /* Here the 'if' is needless for logic, but let make sure
1792 * iter is valid for gtk_tree_model_get. */
1793 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1795 gtk_tree_model_get (priv->header_model, &tmp_iter,
1796 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1799 if (msg_is_visible (header, priv->is_outbox))
1800 is_first_selected = FALSE;
1801 g_object_unref(G_OBJECT(header));
1804 gtk_tree_path_free (path);
1805 return is_first_selected;
1810 GtkTreeRowReference *row_reference;
1814 message_reader_performer (gboolean canceled,
1816 GtkWindow *parent_window,
1817 TnyAccount *account,
1820 ModestMailOperation *mail_op = NULL;
1821 MsgReaderInfo *info;
1823 info = (MsgReaderInfo *) user_data;
1824 if (canceled || err) {
1825 update_window_title (MODEST_MSG_VIEW_WINDOW (parent_window));
1829 /* Register the header - it'll be unregistered in the callback */
1830 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1832 /* New mail operation */
1833 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1834 modest_ui_actions_disk_operations_error_handler,
1837 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1838 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1839 g_object_unref (mail_op);
1841 /* Update dimming rules */
1842 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1843 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1846 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1847 g_object_unref (info->header);
1848 g_slice_free (MsgReaderInfo, info);
1853 * Reads the message whose summary item is @header. It takes care of
1854 * several things, among others:
1856 * If the message was not previously downloaded then ask the user
1857 * before downloading. If there is no connection launch the connection
1858 * dialog. Update toolbar dimming rules.
1860 * Returns: TRUE if the mail operation was started, otherwise if the
1861 * user do not want to download the message, or if the user do not
1862 * want to connect, then the operation is not issued
1865 message_reader (ModestMsgViewWindow *window,
1866 ModestMsgViewWindowPrivate *priv,
1868 GtkTreeRowReference *row_reference)
1870 ModestWindowMgr *mgr;
1871 TnyAccount *account;
1873 MsgReaderInfo *info;
1875 g_return_val_if_fail (row_reference != NULL, FALSE);
1877 mgr = modest_runtime_get_window_mgr ();
1878 /* Msg download completed */
1879 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1881 /* We set the header from model while we're loading */
1882 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1883 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1885 /* Ask the user if he wants to download the message if
1887 if (!tny_device_is_online (modest_runtime_get_device())) {
1888 GtkResponseType response;
1890 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1891 _("mcen_nc_get_msg"));
1892 if (response == GTK_RESPONSE_CANCEL) {
1893 update_window_title (window);
1897 folder = tny_header_get_folder (header);
1898 info = g_slice_new (MsgReaderInfo);
1899 info->header = g_object_ref (header);
1900 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1902 /* Offer the connection dialog if necessary */
1903 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1905 TNY_FOLDER_STORE (folder),
1906 message_reader_performer,
1908 g_object_unref (folder);
1913 folder = tny_header_get_folder (header);
1914 account = tny_folder_get_account (folder);
1915 info = g_slice_new (MsgReaderInfo);
1916 info->header = g_object_ref (header);
1917 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1919 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1920 g_object_unref (account);
1921 g_object_unref (folder);
1927 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1929 ModestMsgViewWindowPrivate *priv;
1930 GtkTreePath *path= NULL;
1931 GtkTreeIter tmp_iter;
1933 gboolean retval = TRUE;
1934 GtkTreeRowReference *row_reference = NULL;
1936 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1937 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1939 if (!priv->row_reference)
1942 /* Update the next row reference if it's not valid. This could
1943 happen if for example the header which it was pointing to,
1944 was deleted. The best place to do it is in the row-deleted
1945 handler but the tinymail model do not work like the glib
1946 tree models and reports the deletion when the row is still
1948 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1949 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1950 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1951 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1954 if (priv->next_row_reference)
1955 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1959 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1961 gtk_tree_model_get_iter (priv->header_model,
1964 gtk_tree_path_free (path);
1966 gtk_tree_model_get (priv->header_model, &tmp_iter,
1967 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1970 /* Read the message & show it */
1971 if (!message_reader (window, priv, header, row_reference)) {
1974 gtk_tree_row_reference_free (row_reference);
1977 g_object_unref (header);
1983 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1985 ModestMsgViewWindowPrivate *priv = NULL;
1987 gboolean finished = FALSE;
1988 gboolean retval = FALSE;
1990 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1991 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1993 /* Return inmediatly if there is no header model */
1994 if (!priv->header_model || !priv->row_reference)
1997 path = gtk_tree_row_reference_get_path (priv->row_reference);
1998 while (!finished && gtk_tree_path_prev (path)) {
2002 gtk_tree_model_get_iter (priv->header_model, &iter, path);
2003 gtk_tree_model_get (priv->header_model, &iter,
2004 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2008 if (msg_is_visible (header, priv->is_outbox)) {
2009 GtkTreeRowReference *row_reference;
2010 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2011 /* Read the message & show it */
2012 retval = message_reader (window, priv, header, row_reference);
2013 gtk_tree_row_reference_free (row_reference);
2017 g_object_unref (header);
2021 gtk_tree_path_free (path);
2026 view_msg_cb (ModestMailOperation *mail_op,
2033 ModestMsgViewWindow *self = NULL;
2034 ModestMsgViewWindowPrivate *priv = NULL;
2035 GtkTreeRowReference *row_reference = NULL;
2037 /* Unregister the header (it was registered before creating the mail operation) */
2038 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2040 row_reference = (GtkTreeRowReference *) user_data;
2042 gtk_tree_row_reference_free (row_reference);
2043 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2045 /* Restore window title */
2046 update_window_title (self);
2047 g_object_unref (self);
2052 /* If there was any error */
2053 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2054 gtk_tree_row_reference_free (row_reference);
2055 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2057 /* Restore window title */
2058 update_window_title (self);
2059 g_object_unref (self);
2064 /* Get the window */
2065 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2066 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2067 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2069 /* Update the row reference */
2070 if (priv->row_reference != NULL) {
2071 gtk_tree_row_reference_free (priv->row_reference);
2072 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2073 if (priv->next_row_reference != NULL) {
2074 gtk_tree_row_reference_free (priv->next_row_reference);
2076 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2077 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2080 /* Mark header as read */
2081 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2082 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2084 /* Set new message */
2085 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2086 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2087 modest_msg_view_window_update_priority (self);
2088 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2089 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2092 /* Set the new message uid of the window */
2093 if (priv->msg_uid) {
2094 g_free (priv->msg_uid);
2095 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2098 /* Notify the observers */
2099 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2100 0, priv->header_model, priv->row_reference);
2103 g_object_unref (self);
2104 gtk_tree_row_reference_free (row_reference);
2108 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2110 ModestMsgViewWindowPrivate *priv;
2112 TnyFolderType folder_type;
2114 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2116 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2118 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2122 folder = tny_msg_get_folder (msg);
2124 folder_type = modest_tny_folder_guess_folder_type (folder);
2125 g_object_unref (folder);
2127 g_object_unref (msg);
2135 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2137 ModestMsgViewWindowPrivate *priv;
2138 TnyHeader *header = NULL;
2139 TnyHeaderFlags flags = 0;
2141 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2143 if (priv->header_model && priv->row_reference) {
2145 GtkTreePath *path = NULL;
2147 path = gtk_tree_row_reference_get_path (priv->row_reference);
2148 g_return_if_fail (path != NULL);
2149 gtk_tree_model_get_iter (priv->header_model,
2151 gtk_tree_row_reference_get_path (priv->row_reference));
2153 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2155 gtk_tree_path_free (path);
2158 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2160 header = tny_msg_get_header (msg);
2161 g_object_unref (msg);
2166 flags = tny_header_get_flags (header);
2167 g_object_unref(G_OBJECT(header));
2170 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2175 toolbar_resize (ModestMsgViewWindow *self)
2177 ModestMsgViewWindowPrivate *priv = NULL;
2178 ModestWindowPrivate *parent_priv = NULL;
2180 gint static_button_size;
2181 ModestWindowMgr *mgr;
2183 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2184 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2185 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2187 mgr = modest_runtime_get_window_mgr ();
2188 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?120:120;
2190 if (parent_priv->toolbar) {
2191 /* left size buttons */
2192 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2193 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2194 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2195 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2196 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2197 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2198 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2199 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2200 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2201 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2202 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2203 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2204 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2205 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2206 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2207 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2209 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2210 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2211 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2212 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2217 modest_msg_view_window_show_toolbar (ModestWindow *self,
2218 gboolean show_toolbar)
2220 ModestMsgViewWindowPrivate *priv = NULL;
2221 ModestWindowPrivate *parent_priv;
2223 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2224 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2226 /* Set optimized view status */
2227 priv->optimized_view = !show_toolbar;
2229 if (!parent_priv->toolbar) {
2230 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2232 gtk_toolbar_set_icon_size (GTK_TOOLBAR (parent_priv->toolbar), HILDON_ICON_SIZE_FINGER);
2233 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2235 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2236 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2237 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2240 hildon_window_add_toolbar (HILDON_WINDOW (self),
2241 GTK_TOOLBAR (parent_priv->toolbar));
2246 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2247 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2248 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2250 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2251 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2252 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2254 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2257 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2258 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2263 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2265 ModestMsgViewWindow *window)
2267 if (!GTK_WIDGET_VISIBLE (window))
2270 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2274 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2276 ModestMsgViewWindowPrivate *priv;
2278 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2279 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2281 return priv->progress_hint;
2285 observers_empty (ModestMsgViewWindow *self)
2288 ModestMsgViewWindowPrivate *priv;
2289 gboolean is_empty = TRUE;
2290 guint pending_ops = 0;
2292 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2293 tmp = priv->progress_widgets;
2295 /* Check all observers */
2296 while (tmp && is_empty) {
2297 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2298 is_empty = pending_ops == 0;
2300 tmp = g_slist_next(tmp);
2307 on_account_removed (TnyAccountStore *account_store,
2308 TnyAccount *account,
2311 /* Do nothing if it's a transport account, because we only
2312 show the messages of a store account */
2313 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2314 const gchar *parent_acc = NULL;
2315 const gchar *our_acc = NULL;
2317 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2318 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2320 /* Close this window if I'm showing a message of the removed account */
2321 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2322 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2327 on_mail_operation_started (ModestMailOperation *mail_op,
2330 ModestMsgViewWindow *self;
2331 ModestMailOperationTypeOperation op_type;
2333 ModestMsgViewWindowPrivate *priv;
2334 GObject *source = NULL;
2336 self = MODEST_MSG_VIEW_WINDOW (user_data);
2337 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2338 op_type = modest_mail_operation_get_type_operation (mail_op);
2339 tmp = priv->progress_widgets;
2340 source = modest_mail_operation_get_source(mail_op);
2341 if (G_OBJECT (self) == source) {
2342 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2343 set_toolbar_transfer_mode(self);
2345 modest_progress_object_add_operation (
2346 MODEST_PROGRESS_OBJECT (tmp->data),
2348 tmp = g_slist_next (tmp);
2352 g_object_unref (source);
2356 on_mail_operation_finished (ModestMailOperation *mail_op,
2359 ModestMsgViewWindow *self;
2360 ModestMailOperationTypeOperation op_type;
2362 ModestMsgViewWindowPrivate *priv;
2364 self = MODEST_MSG_VIEW_WINDOW (user_data);
2365 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2366 op_type = modest_mail_operation_get_type_operation (mail_op);
2367 tmp = priv->progress_widgets;
2369 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE) {
2371 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2373 tmp = g_slist_next (tmp);
2376 /* If no more operations are being observed, NORMAL mode is enabled again */
2377 if (observers_empty (self)) {
2378 set_progress_hint (self, FALSE);
2382 /* Update dimming rules. We have to do this right here
2383 and not in view_msg_cb because at that point the
2384 transfer mode is still enabled so the dimming rule
2385 won't let the user delete the message that has been
2386 readed for example */
2387 check_dimming_rules_after_change (self);
2392 on_queue_changed (ModestMailOperationQueue *queue,
2393 ModestMailOperation *mail_op,
2394 ModestMailOperationQueueNotification type,
2395 ModestMsgViewWindow *self)
2397 ModestMsgViewWindowPrivate *priv;
2399 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2401 /* If this operations was created by another window, do nothing */
2402 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2405 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2406 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2408 "operation-started",
2409 G_CALLBACK (on_mail_operation_started),
2411 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2413 "operation-finished",
2414 G_CALLBACK (on_mail_operation_finished),
2416 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2417 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2419 "operation-started");
2420 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2422 "operation-finished");
2427 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2429 ModestMsgViewWindowPrivate *priv;
2430 TnyList *selected_attachments = NULL;
2432 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2433 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2435 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2436 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2438 return selected_attachments;
2442 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2448 gchar *filepath = (gchar *) user_data;
2450 if (cancelled || err) {
2452 modest_platform_information_banner (NULL, NULL,
2453 _KR("cerm_device_memory_full"));
2458 /* make the file read-only */
2459 g_chmod(filepath, 0444);
2461 /* Activate the file */
2462 modest_platform_activate_file (filepath, tny_mime_part_get_content_type (mime_part));
2470 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2471 TnyMimePart *mime_part)
2473 ModestMsgViewWindowPrivate *priv;
2474 const gchar *msg_uid;
2475 gchar *attachment_uid = NULL;
2476 gint attachment_index = 0;
2477 TnyList *attachments;
2479 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2480 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2481 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2483 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2484 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2485 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2486 g_object_unref (attachments);
2488 if (msg_uid && attachment_index >= 0) {
2489 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2492 if (mime_part == NULL) {
2493 gboolean error = FALSE;
2494 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2495 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2497 } else if (tny_list_get_length (selected_attachments) > 1) {
2498 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2502 iter = tny_list_create_iterator (selected_attachments);
2503 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2504 g_object_unref (iter);
2506 if (selected_attachments)
2507 g_object_unref (selected_attachments);
2512 g_object_ref (mime_part);
2515 if (tny_mime_part_is_purged (mime_part))
2518 if (!modest_tny_mime_part_is_msg (mime_part)) {
2519 gchar *filepath = NULL;
2520 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2521 gboolean show_error_banner = FALSE;
2522 TnyFsStream *temp_stream = NULL;
2523 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2526 if (temp_stream != NULL) {
2527 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2528 on_decode_to_stream_async_handler,
2530 g_strdup (filepath));
2531 g_object_unref (temp_stream);
2532 /* NOTE: files in the temporary area will be automatically
2533 * cleaned after some time if they are no longer in use */
2536 const gchar *content_type;
2537 /* the file may already exist but it isn't writable,
2538 * let's try to open it anyway */
2539 content_type = tny_mime_part_get_content_type (mime_part);
2540 modest_platform_activate_file (filepath, content_type);
2542 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2543 show_error_banner = TRUE;
2548 if (show_error_banner)
2549 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2551 /* message attachment */
2552 TnyHeader *header = NULL;
2553 ModestWindowMgr *mgr;
2554 ModestWindow *msg_win = NULL;
2557 header = tny_msg_get_header (TNY_MSG (mime_part));
2558 mgr = modest_runtime_get_window_mgr ();
2559 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2562 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2563 * thus, we don't do anything */
2564 g_warning ("window for is already being created");
2566 /* it's not found, so create a new window for it */
2567 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2568 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2569 const gchar *mailbox = modest_window_get_active_mailbox (MODEST_WINDOW (window));
2571 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2572 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2573 mailbox, attachment_uid);
2574 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2575 modest_window_get_zoom (MODEST_WINDOW (window)));
2576 if (modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window)))
2577 gtk_widget_show_all (GTK_WIDGET (msg_win));
2579 gtk_widget_destroy (GTK_WIDGET (msg_win));
2585 g_free (attachment_uid);
2587 g_object_unref (mime_part);
2599 GnomeVFSResult result;
2602 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2603 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2604 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2605 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2608 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2612 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2613 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2614 g_free (pair->filename);
2615 g_object_unref (pair->part);
2616 g_slice_free (SaveMimePartPair, pair);
2618 g_list_free (info->pairs);
2621 g_slice_free (SaveMimePartInfo, info);
2626 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2628 if (info->pairs != NULL) {
2629 save_mime_part_to_file (info);
2631 /* This is a GDK lock because we are an idle callback and
2632 * hildon_banner_show_information is or does Gtk+ code */
2634 gdk_threads_enter (); /* CHECKED */
2635 save_mime_part_info_free (info, TRUE);
2636 if (info->result == GNOME_VFS_OK) {
2637 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2638 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2639 hildon_banner_show_information (NULL, NULL,
2640 _KR("cerm_device_memory_full"));
2642 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2644 gdk_threads_leave (); /* CHECKED */
2651 save_mime_part_to_file (SaveMimePartInfo *info)
2653 GnomeVFSHandle *handle;
2655 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2657 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2658 if (info->result == GNOME_VFS_OK) {
2659 GError *error = NULL;
2660 stream = tny_vfs_stream_new (handle);
2661 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2662 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2664 if ((error->domain == TNY_ERROR_DOMAIN) &&
2665 (error->code == TNY_IO_ERROR_WRITE) &&
2666 (errno == ENOSPC)) {
2667 info->result = GNOME_VFS_ERROR_NO_SPACE;
2669 info->result = GNOME_VFS_ERROR_IO;
2672 g_object_unref (G_OBJECT (stream));
2673 g_object_unref (pair->part);
2674 g_slice_free (SaveMimePartPair, pair);
2675 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2677 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2678 save_mime_part_info_free (info, FALSE);
2681 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2686 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2688 gboolean is_ok = TRUE;
2689 gint replaced_files = 0;
2690 const GList *files = info->pairs;
2693 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2694 SaveMimePartPair *pair = iter->data;
2695 if (modest_utils_file_exists (pair->filename)) {
2699 if (replaced_files) {
2700 GtkWidget *confirm_overwrite_dialog;
2701 const gchar *message = (replaced_files == 1) ?
2702 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2703 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2704 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2707 gtk_widget_destroy (confirm_overwrite_dialog);
2711 save_mime_part_info_free (info, TRUE);
2713 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2719 save_attachments_response (GtkDialog *dialog,
2723 TnyList *mime_parts;
2725 GList *files_to_save = NULL;
2726 gchar *current_folder;
2728 mime_parts = TNY_LIST (user_data);
2730 if (arg1 != GTK_RESPONSE_OK)
2733 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2734 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (dialog));
2735 if (current_folder && current_folder != '\0') {
2737 modest_conf_set_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH,
2738 current_folder,&err);
2740 g_debug ("Error storing latest used folder: %s", err->message);
2744 g_free (current_folder);
2746 if (!modest_utils_folder_writable (chooser_uri)) {
2747 hildon_banner_show_information
2748 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2752 iter = tny_list_create_iterator (mime_parts);
2753 while (!tny_iterator_is_done (iter)) {
2754 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2756 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2757 !tny_mime_part_is_purged (mime_part) &&
2758 (tny_mime_part_get_filename (mime_part) != NULL)) {
2759 SaveMimePartPair *pair;
2761 pair = g_slice_new0 (SaveMimePartPair);
2763 if (tny_list_get_length (mime_parts) > 1) {
2765 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2766 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2769 pair->filename = g_strdup (chooser_uri);
2771 pair->part = mime_part;
2772 files_to_save = g_list_prepend (files_to_save, pair);
2774 tny_iterator_next (iter);
2776 g_object_unref (iter);
2778 g_free (chooser_uri);
2780 if (files_to_save != NULL) {
2781 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2782 info->pairs = files_to_save;
2783 info->result = TRUE;
2784 save_mime_parts_to_file_with_checks (info);
2788 /* Free and close the dialog */
2789 g_object_unref (mime_parts);
2790 gtk_widget_destroy (GTK_WIDGET (dialog));
2794 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window,
2795 TnyList *mime_parts)
2797 ModestMsgViewWindowPrivate *priv;
2798 GtkWidget *save_dialog = NULL;
2799 gchar *conf_folder = NULL;
2800 gchar *filename = NULL;
2801 gchar *save_multiple_str = NULL;
2803 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2804 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2806 if (mime_parts == NULL) {
2807 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2808 * selection available */
2809 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2810 if (mime_parts && !modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, FALSE)) {
2811 g_object_unref (mime_parts);
2814 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) {
2816 g_object_unref (mime_parts);
2822 g_object_ref (mime_parts);
2825 /* prepare dialog */
2826 if (tny_list_get_length (mime_parts) == 1) {
2828 /* only one attachment selected */
2829 iter = tny_list_create_iterator (mime_parts);
2830 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2831 g_object_unref (iter);
2832 if (!modest_tny_mime_part_is_msg (mime_part) &&
2833 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2834 !tny_mime_part_is_purged (mime_part)) {
2835 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2837 /* TODO: show any error? */
2838 g_warning ("Tried to save a non-file attachment");
2839 g_object_unref (mime_parts);
2842 g_object_unref (mime_part);
2844 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2845 tny_list_get_length (mime_parts));
2848 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2849 GTK_FILE_CHOOSER_ACTION_SAVE);
2852 conf_folder = modest_conf_get_string (modest_runtime_get_conf (), MODEST_CONF_LATEST_SAVE_ATTACHMENT_PATH, NULL);
2853 if (conf_folder && conf_folder[0] != '\0') {
2854 gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (save_dialog), conf_folder);
2857 /* Set the default folder to images folder */
2858 docs_folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2859 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), docs_folder);
2860 g_free (docs_folder);
2862 g_free (conf_folder);
2866 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2871 /* if multiple, set multiple string */
2872 if (save_multiple_str) {
2873 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2874 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2877 /* We must run this asynchronously, because the hildon dialog
2878 performs a gtk_dialog_run by itself which leads to gdk
2880 g_signal_connect (save_dialog, "response",
2881 G_CALLBACK (save_attachments_response), mime_parts);
2883 gtk_widget_show_all (save_dialog);
2887 show_remove_attachment_information (gpointer userdata)
2889 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2890 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2892 /* We're outside the main lock */
2893 gdk_threads_enter ();
2895 if (priv->remove_attachment_banner != NULL) {
2896 gtk_widget_destroy (priv->remove_attachment_banner);
2897 g_object_unref (priv->remove_attachment_banner);
2900 priv->remove_attachment_banner = g_object_ref (
2901 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2903 gdk_threads_leave ();
2909 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2911 ModestMsgViewWindowPrivate *priv;
2912 TnyList *mime_parts = NULL;
2913 gchar *confirmation_message;
2919 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2920 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2922 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2923 * because we don't have selection
2925 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2927 /* Remove already purged messages from mime parts list */
2928 iter = tny_list_create_iterator (mime_parts);
2929 while (!tny_iterator_is_done (iter)) {
2930 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2931 tny_iterator_next (iter);
2932 if (tny_mime_part_is_purged (part)) {
2933 tny_list_remove (mime_parts, (GObject *) part);
2935 g_object_unref (part);
2937 g_object_unref (iter);
2939 if (!modest_maemo_utils_select_attachments (GTK_WINDOW (window), mime_parts, TRUE) ||
2940 tny_list_get_length (mime_parts) == 0) {
2941 g_object_unref (mime_parts);
2945 n_attachments = tny_list_get_length (mime_parts);
2946 if (n_attachments == 1) {
2950 iter = tny_list_create_iterator (mime_parts);
2951 part = (TnyMimePart *) tny_iterator_get_current (iter);
2952 g_object_unref (iter);
2953 if (modest_tny_mime_part_is_msg (part)) {
2955 header = tny_msg_get_header (TNY_MSG (part));
2956 filename = tny_header_dup_subject (header);
2957 g_object_unref (header);
2958 if (filename == NULL)
2959 filename = g_strdup (_("mail_va_no_subject"));
2961 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2963 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2965 g_object_unref (part);
2967 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2968 "mcen_nc_purge_files_text",
2969 n_attachments), n_attachments);
2971 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2972 confirmation_message);
2973 g_free (confirmation_message);
2975 if (response != GTK_RESPONSE_OK) {
2976 g_object_unref (mime_parts);
2980 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2982 iter = tny_list_create_iterator (mime_parts);
2983 while (!tny_iterator_is_done (iter)) {
2986 part = (TnyMimePart *) tny_iterator_get_current (iter);
2987 tny_mime_part_set_purged (TNY_MIME_PART (part));
2988 g_object_unref (part);
2989 tny_iterator_next (iter);
2991 g_object_unref (iter);
2993 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2994 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2995 tny_msg_rewrite_cache (msg);
2996 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2997 g_object_unref (msg);
2999 g_object_unref (mime_parts);
3001 if (priv->purge_timeout > 0) {
3002 g_source_remove (priv->purge_timeout);
3003 priv->purge_timeout = 0;
3006 if (priv->remove_attachment_banner) {
3007 gtk_widget_destroy (priv->remove_attachment_banner);
3008 g_object_unref (priv->remove_attachment_banner);
3009 priv->remove_attachment_banner = NULL;
3017 update_window_title (ModestMsgViewWindow *window)
3019 ModestMsgViewWindowPrivate *priv;
3021 TnyHeader *header = NULL;
3022 gchar *subject = NULL;
3024 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3026 /* Note that if the window is closed while we're retrieving
3027 the message, this widget could de deleted */
3028 if (!priv->msg_view)
3031 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3034 header = tny_msg_get_header (msg);
3035 subject = tny_header_dup_subject (header);
3036 g_object_unref (header);
3037 g_object_unref (msg);
3040 if ((subject == NULL)||(subject[0] == '\0')) {
3042 subject = g_strdup (_("mail_va_no_subject"));
3045 gtk_window_set_title (GTK_WINDOW (window), subject);
3050 on_move_focus (GtkWidget *widget,
3051 GtkDirectionType direction,
3054 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3058 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3060 GnomeVFSResult result;
3061 GnomeVFSHandle *handle = NULL;
3062 GnomeVFSFileInfo *info = NULL;
3065 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3066 if (result != GNOME_VFS_OK) {
3071 info = gnome_vfs_file_info_new ();
3072 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3073 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3074 /* We put a "safe" default size for going to cache */
3075 *expected_size = (300*1024);
3077 *expected_size = info->size;
3079 gnome_vfs_file_info_unref (info);
3081 stream = tny_vfs_stream_new (handle);
3090 TnyStream *output_stream;
3091 GtkWidget *msg_view;
3095 on_fetch_image_idle_refresh_view (gpointer userdata)
3098 FetchImageData *fidata = (FetchImageData *) userdata;
3099 g_message ("REFRESH VIEW");
3101 gdk_threads_enter ();
3102 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3103 g_message ("QUEUING DRAW");
3104 gtk_widget_queue_draw (fidata->msg_view);
3106 gdk_threads_leave ();
3108 g_object_unref (fidata->msg_view);
3109 g_slice_free (FetchImageData, fidata);
3114 on_fetch_image_thread (gpointer userdata)
3116 FetchImageData *fidata = (FetchImageData *) userdata;
3117 TnyStreamCache *cache;
3118 TnyStream *cache_stream;
3120 cache = modest_runtime_get_images_cache ();
3122 tny_stream_cache_get_stream (cache,
3124 (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream,
3125 (gpointer) fidata->uri);
3126 g_free (fidata->cache_id);
3127 g_free (fidata->uri);
3129 if (cache_stream != NULL) {
3132 while (G_LIKELY (!tny_stream_is_eos (cache_stream))) {
3135 nb_read = tny_stream_read (cache_stream, buffer, sizeof (buffer));
3136 if (G_UNLIKELY (nb_read < 0)) {
3138 } else if (G_LIKELY (nb_read > 0)) {
3139 gssize nb_written = 0;
3141 while (G_UNLIKELY (nb_written < nb_read)) {
3144 len = tny_stream_write (fidata->output_stream, buffer + nb_written,
3145 nb_read - nb_written);
3146 if (G_UNLIKELY (len < 0))
3152 tny_stream_close (cache_stream);
3153 g_object_unref (cache_stream);
3156 tny_stream_close (fidata->output_stream);
3157 g_object_unref (fidata->output_stream);
3159 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3165 on_fetch_image (ModestMsgView *msgview,
3168 ModestMsgViewWindow *window)
3170 const gchar *current_account;
3171 ModestMsgViewWindowPrivate *priv;
3172 FetchImageData *fidata;
3174 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3176 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3178 fidata = g_slice_new0 (FetchImageData);
3179 fidata->msg_view = g_object_ref (msgview);
3180 fidata->uri = g_strdup (uri);
3181 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3182 fidata->output_stream = g_object_ref (stream);
3184 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3185 g_object_unref (fidata->output_stream);
3186 g_free (fidata->cache_id);
3187 g_free (fidata->uri);
3188 g_object_unref (fidata->msg_view);
3189 g_slice_free (FetchImageData, fidata);
3190 tny_stream_close (stream);
3198 setup_menu (ModestMsgViewWindow *self)
3200 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3202 /* Settings menu buttons */
3203 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3204 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3205 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3206 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3207 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3208 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3210 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3211 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3212 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3213 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3214 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3215 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3217 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3218 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3219 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3220 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3221 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3222 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3224 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3225 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3226 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3227 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3228 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3229 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3233 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3235 ModestMsgViewWindowPrivate *priv;
3236 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3237 GSList *recipients = NULL;
3239 gboolean contacts_to_add = FALSE;
3241 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3245 header = modest_msg_view_window_get_header (self);
3248 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3249 g_object_unref (header);
3251 recipients = modest_tny_msg_get_all_recipients_list (msg);
3252 g_object_unref (msg);
3255 if (recipients != NULL) {
3256 GtkWidget *picker_dialog;
3257 GtkWidget *selector;
3259 gchar *selected = NULL;
3261 selector = hildon_touch_selector_new_text ();
3262 g_object_ref (selector);
3264 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3265 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3266 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3267 (const gchar *) node->data);
3268 contacts_to_add = TRUE;
3272 if (contacts_to_add) {
3275 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3276 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3278 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3279 HILDON_TOUCH_SELECTOR (selector));
3281 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3283 if (picker_result == GTK_RESPONSE_OK) {
3284 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3286 gtk_widget_destroy (picker_dialog);
3289 modest_address_book_add_address (selected);
3294 g_object_unref (selector);
3299 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}
3303 _modest_msg_view_window_map_event (GtkWidget *widget,
3307 ModestMsgViewWindow *self = (ModestMsgViewWindow *) userdata;
3308 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3310 if (priv->progress_hint) {
3311 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), TRUE);
3314 _make_zoom_buttons_grabeable (GTK_WIDGET (self));
3319 _make_zoom_buttons_grabeable (GtkWidget* widget)
3321 GdkDisplay *display;
3323 unsigned long val = 1;
3325 display = gdk_drawable_get_display (widget->window);
3326 atom = gdk_x11_get_xatom_by_name_for_display (display, "_HILDON_ZOOM_KEY_ATOM");
3327 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
3328 GDK_WINDOW_XID (widget->window), atom,
3329 XA_INTEGER, 32, PropModeReplace,
3330 (unsigned char *) &val, 1);