1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include <tny-error.h>
37 #include "modest-marshal.h"
38 #include "modest-platform.h"
39 #include <modest-utils.h>
40 #include <modest-maemo-utils.h>
41 #include <modest-tny-msg.h>
42 #include <modest-msg-view-window.h>
43 #include <modest-main-window-ui.h>
44 #include "modest-msg-view-window-ui-dimming.h"
45 #include <modest-widget-memory.h>
46 #include <modest-progress-object.h>
47 #include <modest-runtime.h>
48 #include <modest-window-priv.h>
49 #include <modest-tny-folder.h>
50 #include <modest-text-utils.h>
51 #include <modest-account-mgr-helpers.h>
52 #include <hildon/hildon-pannable-area.h>
53 #include <hildon/hildon-picker-dialog.h>
54 #include <hildon/hildon-app-menu.h>
55 #include "modest-defs.h"
56 #include "modest-hildon-includes.h"
57 #include "modest-ui-dimming-manager.h"
58 #include <gdk/gdkkeysyms.h>
59 #include <modest-tny-account.h>
60 #include <modest-mime-part-view.h>
61 #include <modest-isearch-view.h>
62 #include <modest-tny-mime-part.h>
63 #include <modest-address-book.h>
66 #include <glib/gstdio.h>
67 #include <modest-debug.h>
68 #include <modest-header-window.h>
70 #define MYDOCS_ENV "MYDOCSDIR"
71 #define DOCS_FOLDER ".documents"
73 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
74 struct _ModestMsgViewWindowPrivate {
77 GtkWidget *main_scroll;
78 GtkWidget *find_toolbar;
81 /* Progress observers */
82 GSList *progress_widgets;
85 GtkWidget *prev_toolitem;
86 GtkWidget *next_toolitem;
87 gboolean progress_hint;
89 /* Optimized view enabled */
90 gboolean optimized_view;
92 /* Whether this was created via the *_new_for_search_result() function. */
93 gboolean is_search_result;
95 /* Whether the message is in outbox */
98 /* A reference to the @model of the header view
99 * to allow selecting previous/next messages,
100 * if the message is currently selected in the header view.
102 const gchar *header_folder_id;
103 GtkTreeModel *header_model;
104 GtkTreeRowReference *row_reference;
105 GtkTreeRowReference *next_row_reference;
107 gulong clipboard_change_handler;
108 gulong queue_change_handler;
109 gulong account_removed_handler;
110 gulong row_changed_handler;
111 gulong row_deleted_handler;
112 gulong row_inserted_handler;
113 gulong rows_reordered_handler;
116 GtkWidget *remove_attachment_banner;
123 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
124 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
125 static void modest_header_view_observer_init(
126 ModestHeaderViewObserverIface *iface_class);
127 static void modest_msg_view_window_finalize (GObject *obj);
128 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
130 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
131 ModestMsgViewWindow *obj);
132 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
133 ModestMsgViewWindow *obj);
135 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
137 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
138 static void modest_msg_view_window_set_zoom (ModestWindow *window,
140 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
141 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
142 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
145 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
147 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
148 gboolean show_toolbar);
150 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
152 ModestMsgViewWindow *window);
154 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
157 ModestMsgViewWindow *window);
159 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
161 ModestMsgViewWindow *window);
163 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
164 GtkTreePath *tree_path,
165 GtkTreeIter *tree_iter,
166 ModestMsgViewWindow *window);
168 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
172 ModestMsgViewWindow *window);
174 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
176 const gchar *tny_folder_id);
178 static void on_queue_changed (ModestMailOperationQueue *queue,
179 ModestMailOperation *mail_op,
180 ModestMailOperationQueueNotification type,
181 ModestMsgViewWindow *self);
183 static void on_account_removed (TnyAccountStore *account_store,
187 static void on_move_focus (GtkWidget *widget,
188 GtkDirectionType direction,
191 static void view_msg_cb (ModestMailOperation *mail_op,
198 static void set_progress_hint (ModestMsgViewWindow *self,
201 static void update_window_title (ModestMsgViewWindow *window);
203 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
204 static void init_window (ModestMsgViewWindow *obj);
206 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
208 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
210 static gboolean on_fetch_image (ModestMsgView *msgview,
213 ModestMsgViewWindow *window);
215 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
216 GtkScrollType scroll_type,
219 static gboolean message_reader (ModestMsgViewWindow *window,
220 ModestMsgViewWindowPrivate *priv,
222 GtkTreeRowReference *row_reference);
224 static void setup_menu (ModestMsgViewWindow *self);
226 /* list my signals */
233 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
234 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
238 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
239 MODEST_TYPE_MSG_VIEW_WINDOW, \
240 ModestMsgViewWindowPrivate))
242 static GtkWindowClass *parent_class = NULL;
244 /* uncomment the following if you have defined any signals */
245 static guint signals[LAST_SIGNAL] = {0};
248 modest_msg_view_window_get_type (void)
250 static GType my_type = 0;
252 static const GTypeInfo my_info = {
253 sizeof(ModestMsgViewWindowClass),
254 NULL, /* base init */
255 NULL, /* base finalize */
256 (GClassInitFunc) modest_msg_view_window_class_init,
257 NULL, /* class finalize */
258 NULL, /* class data */
259 sizeof(ModestMsgViewWindow),
261 (GInstanceInitFunc) modest_msg_view_window_init,
264 my_type = g_type_register_static (MODEST_TYPE_HILDON2_WINDOW,
265 "ModestMsgViewWindow",
268 static const GInterfaceInfo modest_header_view_observer_info =
270 (GInterfaceInitFunc) modest_header_view_observer_init,
271 NULL, /* interface_finalize */
272 NULL /* interface_data */
275 g_type_add_interface_static (my_type,
276 MODEST_TYPE_HEADER_VIEW_OBSERVER,
277 &modest_header_view_observer_info);
283 save_state (ModestWindow *self)
285 modest_widget_memory_save (modest_runtime_get_conf (),
287 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
291 gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
292 GtkScrollType scroll_type,
296 ModestMsgViewWindowPrivate *priv;
297 gboolean return_value;
299 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
300 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
305 add_scroll_binding (GtkBindingSet *binding_set,
307 GtkScrollType scroll)
309 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
311 gtk_binding_entry_add_signal (binding_set, keyval, 0,
313 GTK_TYPE_SCROLL_TYPE, scroll,
314 G_TYPE_BOOLEAN, FALSE);
315 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
317 GTK_TYPE_SCROLL_TYPE, scroll,
318 G_TYPE_BOOLEAN, FALSE);
322 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
324 GObjectClass *gobject_class;
325 HildonWindowClass *hildon_window_class;
326 ModestWindowClass *modest_window_class;
327 GtkBindingSet *binding_set;
329 gobject_class = (GObjectClass*) klass;
330 hildon_window_class = (HildonWindowClass *) klass;
331 modest_window_class = (ModestWindowClass *) klass;
333 parent_class = g_type_class_peek_parent (klass);
334 gobject_class->finalize = modest_msg_view_window_finalize;
336 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
337 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
338 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
339 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
340 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
341 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
343 modest_window_class->save_state_func = save_state;
345 klass->scroll_child = modest_msg_view_window_scroll_child;
347 signals[MSG_CHANGED_SIGNAL] =
348 g_signal_new ("msg-changed",
349 G_TYPE_FROM_CLASS (gobject_class),
351 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
353 modest_marshal_VOID__POINTER_POINTER,
354 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
356 signals[SCROLL_CHILD_SIGNAL] =
357 g_signal_new ("scroll-child",
358 G_TYPE_FROM_CLASS (gobject_class),
359 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
360 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
362 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
363 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
365 binding_set = gtk_binding_set_by_class (klass);
366 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
367 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
368 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
369 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
370 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
371 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
373 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
377 static void modest_header_view_observer_init(
378 ModestHeaderViewObserverIface *iface_class)
380 iface_class->update_func = modest_msg_view_window_update_model_replaced;
384 modest_msg_view_window_init (ModestMsgViewWindow *obj)
386 ModestMsgViewWindowPrivate *priv;
387 ModestWindowPrivate *parent_priv = NULL;
388 GtkActionGroup *action_group = NULL;
389 GError *error = NULL;
390 GdkPixbuf *window_icon;
392 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
393 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
394 parent_priv->ui_manager = gtk_ui_manager_new();
396 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
397 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
399 /* Add common actions */
400 gtk_action_group_add_actions (action_group,
401 modest_action_entries,
402 G_N_ELEMENTS (modest_action_entries),
404 gtk_action_group_add_toggle_actions (action_group,
405 msg_view_toggle_action_entries,
406 G_N_ELEMENTS (msg_view_toggle_action_entries),
409 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
410 g_object_unref (action_group);
412 /* Load the UI definition */
413 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
416 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
417 g_error_free (error);
422 /* Add accelerators */
423 gtk_window_add_accel_group (GTK_WINDOW (obj),
424 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
426 priv->is_search_result = FALSE;
427 priv->is_outbox = FALSE;
429 priv->msg_view = NULL;
430 priv->header_model = NULL;
431 priv->header_folder_id = NULL;
432 priv->clipboard_change_handler = 0;
433 priv->queue_change_handler = 0;
434 priv->account_removed_handler = 0;
435 priv->row_changed_handler = 0;
436 priv->row_deleted_handler = 0;
437 priv->row_inserted_handler = 0;
438 priv->rows_reordered_handler = 0;
439 priv->progress_hint = FALSE;
441 priv->optimized_view = FALSE;
442 priv->purge_timeout = 0;
443 priv->remove_attachment_banner = NULL;
444 priv->msg_uid = NULL;
446 priv->sighandlers = NULL;
449 init_window (MODEST_MSG_VIEW_WINDOW(obj));
451 /* Set window icon */
452 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
454 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
455 g_object_unref (window_icon);
458 hildon_program_add_window (hildon_program_get_instance(),
465 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
467 ModestMsgViewWindowPrivate *priv = NULL;
469 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
471 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
473 set_progress_hint (self, TRUE);
479 set_progress_hint (ModestMsgViewWindow *self,
482 ModestWindowPrivate *parent_priv;
483 ModestMsgViewWindowPrivate *priv;
485 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
487 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
490 /* Sets current progress hint */
491 priv->progress_hint = enabled;
493 if (GTK_WIDGET_VISIBLE (self)) {
494 hildon_gtk_window_set_progress_indicator (GTK_WINDOW (self), enabled?1:0);
501 init_window (ModestMsgViewWindow *obj)
503 GtkWidget *main_vbox;
504 ModestMsgViewWindowPrivate *priv;
506 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
508 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
509 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
510 main_vbox = gtk_vbox_new (FALSE, 6);
511 #ifdef MODEST_TOOLKIT_HILDON2
512 priv->main_scroll = hildon_pannable_area_new ();
513 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
515 #ifdef MODEST_USE_MOZEMBED
516 priv->main_scroll = priv->msg_view;
517 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
519 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
520 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
522 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
523 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
524 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
527 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
528 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
530 priv->find_toolbar = hildon_find_toolbar_new (NULL);
531 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
532 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
534 gtk_widget_show_all (GTK_WIDGET(main_vbox));
538 modest_msg_view_window_disconnect_signals (ModestWindow *self)
540 ModestMsgViewWindowPrivate *priv;
541 GtkWidget *header_view = NULL;
542 GtkWindow *parent_window = NULL;
544 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
546 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
547 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
548 priv->clipboard_change_handler))
549 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
550 priv->clipboard_change_handler);
552 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
553 priv->queue_change_handler))
554 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
555 priv->queue_change_handler);
557 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
558 priv->account_removed_handler))
559 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
560 priv->account_removed_handler);
562 if (priv->header_model) {
563 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
564 priv->row_changed_handler))
565 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
566 priv->row_changed_handler);
568 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
569 priv->row_deleted_handler))
570 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
571 priv->row_deleted_handler);
573 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
574 priv->row_inserted_handler))
575 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
576 priv->row_inserted_handler);
578 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
579 priv->rows_reordered_handler))
580 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
581 priv->rows_reordered_handler);
584 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
585 priv->sighandlers = NULL;
587 parent_window = gtk_window_get_transient_for (GTK_WINDOW (self));
588 if (parent_window && MODEST_IS_HEADER_WINDOW (parent_window)) {
589 header_view = GTK_WIDGET (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
591 modest_header_view_remove_observer(MODEST_HEADER_VIEW (header_view),
592 MODEST_HEADER_VIEW_OBSERVER(self));
598 modest_msg_view_window_finalize (GObject *obj)
600 ModestMsgViewWindowPrivate *priv;
602 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
604 /* Sanity check: shouldn't be needed, the window mgr should
605 call this function before */
606 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
608 if (priv->header_model != NULL) {
609 g_object_unref (priv->header_model);
610 priv->header_model = NULL;
613 if (priv->remove_attachment_banner) {
614 gtk_widget_destroy (priv->remove_attachment_banner);
615 g_object_unref (priv->remove_attachment_banner);
616 priv->remove_attachment_banner = NULL;
619 if (priv->purge_timeout > 0) {
620 g_source_remove (priv->purge_timeout);
621 priv->purge_timeout = 0;
624 if (priv->row_reference) {
625 gtk_tree_row_reference_free (priv->row_reference);
626 priv->row_reference = NULL;
629 if (priv->next_row_reference) {
630 gtk_tree_row_reference_free (priv->next_row_reference);
631 priv->next_row_reference = NULL;
635 g_free (priv->msg_uid);
636 priv->msg_uid = NULL;
639 G_OBJECT_CLASS(parent_class)->finalize (obj);
643 select_next_valid_row (GtkTreeModel *model,
644 GtkTreeRowReference **row_reference,
648 GtkTreeIter tmp_iter;
650 GtkTreePath *next = NULL;
651 gboolean retval = FALSE, finished;
653 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
655 path = gtk_tree_row_reference_get_path (*row_reference);
656 gtk_tree_model_get_iter (model, &tmp_iter, path);
657 gtk_tree_row_reference_free (*row_reference);
658 *row_reference = NULL;
662 TnyHeader *header = NULL;
664 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
665 gtk_tree_model_get (model, &tmp_iter,
666 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
670 if (msg_is_visible (header, is_outbox)) {
671 next = gtk_tree_model_get_path (model, &tmp_iter);
672 *row_reference = gtk_tree_row_reference_new (model, next);
673 gtk_tree_path_free (next);
677 g_object_unref (header);
680 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
681 next = gtk_tree_model_get_path (model, &tmp_iter);
683 /* Ensure that we are not selecting the same */
684 if (gtk_tree_path_compare (path, next) != 0) {
685 gtk_tree_model_get (model, &tmp_iter,
686 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
689 if (msg_is_visible (header, is_outbox)) {
690 *row_reference = gtk_tree_row_reference_new (model, next);
694 g_object_unref (header);
698 /* If we ended up in the same message
699 then there is no valid next
703 gtk_tree_path_free (next);
705 /* If there are no more messages and we don't
706 want to start again in the first one then
707 there is no valid next message */
713 gtk_tree_path_free (path);
718 /* TODO: This should be in _init(), with the parameters as properties. */
720 modest_msg_view_window_construct (ModestMsgViewWindow *self,
721 const gchar *modest_account_name,
722 const gchar *msg_uid)
725 ModestMsgViewWindowPrivate *priv = NULL;
726 ModestWindowPrivate *parent_priv = NULL;
727 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
728 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
730 obj = G_OBJECT (self);
731 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
732 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
734 priv->msg_uid = g_strdup (msg_uid);
737 parent_priv->menubar = NULL;
739 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
740 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
743 /* Add common dimming rules */
744 modest_dimming_rules_group_add_rules (toolbar_rules_group,
745 modest_msg_view_toolbar_dimming_entries,
746 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
747 MODEST_WINDOW (self));
748 modest_dimming_rules_group_add_rules (clipboard_rules_group,
749 modest_msg_view_clipboard_dimming_entries,
750 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
751 MODEST_WINDOW (self));
753 /* Insert dimming rules group for this window */
754 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
755 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
756 g_object_unref (toolbar_rules_group);
757 g_object_unref (clipboard_rules_group);
759 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
761 priv->clipboard_change_handler = g_signal_connect (G_OBJECT (gtk_clipboard_get (GDK_SELECTION_PRIMARY)), "owner-change", G_CALLBACK (modest_msg_view_window_clipboard_owner_change), obj);
762 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
763 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
764 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
765 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
766 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
767 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
768 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
769 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
770 g_signal_connect (G_OBJECT(priv->msg_view), "show_details",
771 G_CALLBACK (modest_ui_actions_on_details), obj);
772 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
773 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
774 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
775 G_CALLBACK (on_fetch_image), obj);
777 g_signal_connect (G_OBJECT (obj), "key-release-event",
778 G_CALLBACK (modest_msg_view_window_key_event),
781 g_signal_connect (G_OBJECT (obj), "key-press-event",
782 G_CALLBACK (modest_msg_view_window_key_event),
785 g_signal_connect (G_OBJECT (obj), "move-focus",
786 G_CALLBACK (on_move_focus), obj);
788 /* Mail Operation Queue */
789 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
791 G_CALLBACK (on_queue_changed),
794 /* Account manager */
795 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
797 G_CALLBACK(on_account_removed),
800 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
802 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
803 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
804 priv->last_search = NULL;
806 modest_msg_view_window_show_toolbar (MODEST_WINDOW (obj), TRUE);
808 /* Init the clipboard actions dim status */
809 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
811 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
816 /* FIXME: parameter checks */
818 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
819 const gchar *modest_account_name,
820 const gchar *msg_uid,
822 GtkTreeRowReference *row_reference)
824 ModestMsgViewWindow *window = NULL;
825 ModestMsgViewWindowPrivate *priv = NULL;
826 TnyFolder *header_folder = NULL;
827 ModestHeaderView *header_view = NULL;
828 ModestWindow *main_window = NULL;
829 ModestWindowMgr *mgr = NULL;
832 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
835 mgr = modest_runtime_get_window_mgr ();
836 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
837 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
839 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
841 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
843 /* Remember the message list's TreeModel so we can detect changes
844 * and change the list selection when necessary: */
846 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
848 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
849 MODEST_MAIN_WINDOW(main_window),
850 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
853 if (header_view != NULL){
854 header_folder = modest_header_view_get_folder(header_view);
855 /* This could happen if the header folder was
856 unseleted before opening this msg window (for
857 example if the user selects an account in the
858 folder view of the main window */
860 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
861 priv->header_folder_id = tny_folder_get_id(header_folder);
862 g_assert(priv->header_folder_id != NULL);
863 g_object_unref(header_folder);
867 /* Setup row references and connect signals */
868 priv->header_model = g_object_ref (model);
871 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
872 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
873 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
875 priv->row_reference = NULL;
876 priv->next_row_reference = NULL;
879 /* Connect signals */
880 priv->row_changed_handler =
881 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
882 G_CALLBACK(modest_msg_view_window_on_row_changed),
884 priv->row_deleted_handler =
885 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
886 G_CALLBACK(modest_msg_view_window_on_row_deleted),
888 priv->row_inserted_handler =
889 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
890 G_CALLBACK(modest_msg_view_window_on_row_inserted),
892 priv->rows_reordered_handler =
893 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
894 G_CALLBACK(modest_msg_view_window_on_row_reordered),
897 if (header_view != NULL){
898 modest_header_view_add_observer(header_view,
899 MODEST_HEADER_VIEW_OBSERVER(window));
902 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
903 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
905 /* gtk_widget_show_all (GTK_WIDGET (window)); */
906 modest_msg_view_window_update_priority (window);
907 /* Check dimming rules */
908 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
909 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
910 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
912 return MODEST_WINDOW(window);
916 modest_msg_view_window_new_from_header_view (ModestHeaderView *header_view,
917 const gchar *modest_account_name,
918 const gchar *msg_uid,
919 GtkTreeRowReference *row_reference)
921 ModestMsgViewWindow *window = NULL;
922 ModestMsgViewWindowPrivate *priv = NULL;
923 TnyFolder *header_folder = NULL;
924 ModestWindowMgr *mgr = NULL;
928 mgr = modest_runtime_get_window_mgr ();
929 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
930 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
932 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
934 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
936 /* Remember the message list's TreeModel so we can detect changes
937 * and change the list selection when necessary: */
939 if (header_view != NULL){
940 header_folder = modest_header_view_get_folder(header_view);
941 /* This could happen if the header folder was
942 unseleted before opening this msg window (for
943 example if the user selects an account in the
944 folder view of the main window */
946 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
947 priv->header_folder_id = tny_folder_get_id(header_folder);
948 g_assert(priv->header_folder_id != NULL);
949 g_object_unref(header_folder);
953 /* Setup row references and connect signals */
954 priv->header_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
955 g_object_ref (priv->header_model);
958 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
959 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
960 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
962 priv->row_reference = NULL;
963 priv->next_row_reference = NULL;
966 /* Connect signals */
967 priv->row_changed_handler =
968 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-changed",
969 G_CALLBACK(modest_msg_view_window_on_row_changed),
971 priv->row_deleted_handler =
972 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-deleted",
973 G_CALLBACK(modest_msg_view_window_on_row_deleted),
975 priv->row_inserted_handler =
976 g_signal_connect (GTK_TREE_MODEL(priv->header_model), "row-inserted",
977 G_CALLBACK(modest_msg_view_window_on_row_inserted),
979 priv->rows_reordered_handler =
980 g_signal_connect(GTK_TREE_MODEL(priv->header_model), "rows-reordered",
981 G_CALLBACK(modest_msg_view_window_on_row_reordered),
984 if (header_view != NULL){
985 modest_header_view_add_observer(header_view,
986 MODEST_HEADER_VIEW_OBSERVER(window));
989 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), NULL);
991 path = gtk_tree_row_reference_get_path (row_reference);
992 if (gtk_tree_model_get_iter (priv->header_model, &iter, path)) {
994 gtk_tree_model_get (priv->header_model, &iter,
995 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
997 message_reader (window, priv, header, row_reference);
999 gtk_tree_path_free (path);
1001 /* Check dimming rules */
1002 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1003 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1004 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1006 return MODEST_WINDOW(window);
1010 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1011 const gchar *modest_account_name,
1012 const gchar *msg_uid)
1014 ModestMsgViewWindow *window = NULL;
1015 ModestMsgViewWindowPrivate *priv = NULL;
1016 ModestWindowMgr *mgr = NULL;
1018 mgr = modest_runtime_get_window_mgr ();
1019 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1020 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1021 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1023 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1025 /* Remember that this is a search result,
1026 * so we can disable some UI appropriately: */
1027 priv->is_search_result = TRUE;
1029 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1031 update_window_title (window);
1032 /* gtk_widget_show_all (GTK_WIDGET (window));*/
1033 modest_msg_view_window_update_priority (window);
1035 /* Check dimming rules */
1036 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1037 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1038 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1040 return MODEST_WINDOW(window);
1044 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1045 const gchar *modest_account_name,
1046 const gchar *msg_uid)
1048 GObject *obj = NULL;
1049 ModestMsgViewWindowPrivate *priv;
1050 ModestWindowMgr *mgr = NULL;
1052 g_return_val_if_fail (msg, NULL);
1053 mgr = modest_runtime_get_window_mgr ();
1054 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1055 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1056 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1057 modest_account_name, msg_uid);
1059 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1060 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1062 /* gtk_widget_show_all (GTK_WIDGET (obj)); */
1064 /* Check dimming rules */
1065 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1066 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1067 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1069 return MODEST_WINDOW(obj);
1073 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1076 ModestMsgViewWindow *window)
1078 check_dimming_rules_after_change (window);
1082 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1084 ModestMsgViewWindow *window)
1086 check_dimming_rules_after_change (window);
1088 /* The window could have dissapeared */
1091 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1093 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1094 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1098 /* On insertions we check if the folder still has the message we are
1099 * showing or do not. If do not, we do nothing. Which means we are still
1100 * not attached to any header folder and thus next/prev buttons are
1101 * still dimmed. Once the message that is shown by msg-view is found, the
1102 * new model of header-view will be attached and the references will be set.
1103 * On each further insertions dimming rules will be checked. However
1104 * this requires extra CPU time at least works.
1105 * (An message might be deleted from TnyFolder and thus will not be
1106 * inserted into the model again for example if it is removed by the
1107 * imap server and the header view is refreshed.)
1110 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1111 GtkTreePath *tree_path,
1112 GtkTreeIter *tree_iter,
1113 ModestMsgViewWindow *window)
1115 ModestMsgViewWindowPrivate *priv = NULL;
1116 TnyHeader *header = NULL;
1118 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1119 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1121 g_assert (model == priv->header_model);
1123 /* Check if the newly inserted message is the same we are actually
1124 * showing. IF not, we should remain detached from the header model
1125 * and thus prev and next toolbar buttons should remain dimmed. */
1126 gtk_tree_model_get (model, tree_iter,
1127 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1130 if (TNY_IS_HEADER (header)) {
1133 uid = modest_tny_folder_get_header_unique_id (header);
1134 if (!g_str_equal(priv->msg_uid, uid)) {
1135 check_dimming_rules_after_change (window);
1137 g_object_unref (G_OBJECT(header));
1141 g_object_unref(G_OBJECT(header));
1144 if (priv->row_reference) {
1145 gtk_tree_row_reference_free (priv->row_reference);
1148 /* Setup row_reference for the actual msg. */
1149 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1150 if (priv->row_reference == NULL) {
1151 g_warning("No reference for msg header item.");
1155 /* Now set up next_row_reference. */
1156 if (priv->next_row_reference) {
1157 gtk_tree_row_reference_free (priv->next_row_reference);
1160 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1161 select_next_valid_row (priv->header_model,
1162 &(priv->next_row_reference), FALSE, priv->is_outbox);
1164 /* Connect the remaining callbacks to become able to detect
1165 * changes in header-view. */
1166 priv->row_changed_handler =
1167 g_signal_connect (priv->header_model, "row-changed",
1168 G_CALLBACK (modest_msg_view_window_on_row_changed),
1170 priv->row_deleted_handler =
1171 g_signal_connect (priv->header_model, "row-deleted",
1172 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1174 priv->rows_reordered_handler =
1175 g_signal_connect (priv->header_model, "rows-reordered",
1176 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1179 check_dimming_rules_after_change (window);
1183 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1187 ModestMsgViewWindow *window)
1189 ModestMsgViewWindowPrivate *priv = NULL;
1190 gboolean already_changed = FALSE;
1192 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1194 /* If the current row was reordered select the proper next
1195 valid row. The same if the next row reference changes */
1196 if (priv->row_reference &&
1197 gtk_tree_row_reference_valid (priv->row_reference)) {
1199 path = gtk_tree_row_reference_get_path (priv->row_reference);
1200 if (gtk_tree_path_compare (path, arg1) == 0) {
1201 if (priv->next_row_reference) {
1202 gtk_tree_row_reference_free (priv->next_row_reference);
1204 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1205 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1206 already_changed = TRUE;
1208 gtk_tree_path_free (path);
1210 if (!already_changed &&
1211 priv->next_row_reference &&
1212 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1214 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1215 if (gtk_tree_path_compare (path, arg1) == 0) {
1216 if (priv->next_row_reference) {
1217 gtk_tree_row_reference_free (priv->next_row_reference);
1219 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1220 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1222 gtk_tree_path_free (path);
1224 check_dimming_rules_after_change (window);
1227 /* The modest_msg_view_window_update_model_replaced implements update
1228 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1229 * actually belongs to the header-view is the same as the TnyFolder of
1230 * the message of msg-view or not. If they are different, there is
1231 * nothing to do. If they are the same, then the model has replaced and
1232 * the reference in msg-view shall be replaced from the old model to
1233 * the new model. In this case the view will be detached from it's
1234 * header folder. From this point the next/prev buttons are dimmed.
1237 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1238 GtkTreeModel *model,
1239 const gchar *tny_folder_id)
1241 ModestMsgViewWindowPrivate *priv = NULL;
1242 ModestMsgViewWindow *window = NULL;
1244 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1245 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1247 window = MODEST_MSG_VIEW_WINDOW(observer);
1248 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1250 /* If there is an other folder in the header-view then we do
1251 * not care about it's model (msg list). Else if the
1252 * header-view shows the folder the msg shown by us is in, we
1253 * shall replace our model reference and make some check. */
1254 if(model == NULL || tny_folder_id == NULL ||
1255 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1258 /* Model is changed(replaced), so we should forget the old
1259 * one. Because there might be other references and there
1260 * might be some change on the model even if we unreferenced
1261 * it, we need to disconnect our signals here. */
1262 if (priv->header_model) {
1263 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1264 priv->row_changed_handler))
1265 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1266 priv->row_changed_handler);
1267 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1268 priv->row_deleted_handler))
1269 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1270 priv->row_deleted_handler);
1271 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1272 priv->row_inserted_handler))
1273 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1274 priv->row_inserted_handler);
1275 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1276 priv->rows_reordered_handler))
1277 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1278 priv->rows_reordered_handler);
1281 if (priv->row_reference)
1282 gtk_tree_row_reference_free (priv->row_reference);
1283 if (priv->next_row_reference)
1284 gtk_tree_row_reference_free (priv->next_row_reference);
1285 g_object_unref(priv->header_model);
1288 priv->row_changed_handler = 0;
1289 priv->row_deleted_handler = 0;
1290 priv->row_inserted_handler = 0;
1291 priv->rows_reordered_handler = 0;
1292 priv->next_row_reference = NULL;
1293 priv->row_reference = NULL;
1294 priv->header_model = NULL;
1297 priv->header_model = g_object_ref (model);
1299 /* Also we must connect to the new model for row insertions.
1300 * Only for insertions now. We will need other ones only after
1301 * the msg is show by msg-view is added to the new model. */
1302 priv->row_inserted_handler =
1303 g_signal_connect (priv->header_model, "row-inserted",
1304 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1307 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1308 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1312 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1314 ModestMsgViewWindowPrivate *priv= NULL;
1316 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1317 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1319 return priv->progress_hint;
1323 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1325 ModestMsgViewWindowPrivate *priv= NULL;
1327 TnyHeader *header = NULL;
1328 GtkTreePath *path = NULL;
1331 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1332 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1334 /* If the message was not obtained from a treemodel,
1335 * for instance if it was opened directly by the search UI:
1337 if (priv->header_model == NULL ||
1338 priv->row_reference == NULL ||
1339 !gtk_tree_row_reference_valid (priv->row_reference)) {
1340 msg = modest_msg_view_window_get_message (self);
1342 header = tny_msg_get_header (msg);
1343 g_object_unref (msg);
1348 /* Get iter of the currently selected message in the header view: */
1349 path = gtk_tree_row_reference_get_path (priv->row_reference);
1350 g_return_val_if_fail (path != NULL, NULL);
1351 gtk_tree_model_get_iter (priv->header_model,
1355 /* Get current message header */
1356 gtk_tree_model_get (priv->header_model, &iter,
1357 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1360 gtk_tree_path_free (path);
1365 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1367 ModestMsgViewWindowPrivate *priv;
1369 g_return_val_if_fail (self, NULL);
1371 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1373 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1377 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1379 ModestMsgViewWindowPrivate *priv;
1381 g_return_val_if_fail (self, NULL);
1383 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1385 return (const gchar*) priv->msg_uid;
1389 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1392 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1393 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1394 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1398 is_active = gtk_toggle_action_get_active (toggle);
1401 gtk_widget_show (priv->find_toolbar);
1402 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1404 gtk_widget_hide (priv->find_toolbar);
1405 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1408 /* update the toggle buttons status */
1409 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1411 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1416 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1417 ModestMsgViewWindow *obj)
1419 GtkToggleAction *toggle;
1420 ModestWindowPrivate *parent_priv;
1421 ModestMsgViewWindowPrivate *priv;
1423 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1424 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1426 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1427 gtk_toggle_action_set_active (toggle, FALSE);
1428 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1432 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1433 ModestMsgViewWindow *obj)
1435 gchar *current_search;
1436 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1438 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1439 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1443 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1445 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1446 g_free (current_search);
1447 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1451 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1453 g_free (priv->last_search);
1454 priv->last_search = g_strdup (current_search);
1455 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1458 hildon_banner_show_information (NULL, NULL,
1459 _HL("ckct_ib_find_no_matches"));
1460 g_free (priv->last_search);
1461 priv->last_search = NULL;
1463 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1464 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1467 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1468 hildon_banner_show_information (NULL, NULL,
1469 _HL("ckct_ib_find_search_complete"));
1470 g_free (priv->last_search);
1471 priv->last_search = NULL;
1473 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1474 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1478 g_free (current_search);
1483 modest_msg_view_window_set_zoom (ModestWindow *window,
1486 ModestMsgViewWindowPrivate *priv;
1487 ModestWindowPrivate *parent_priv;
1489 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1491 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1492 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1493 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1498 modest_msg_view_window_get_zoom (ModestWindow *window)
1500 ModestMsgViewWindowPrivate *priv;
1502 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1504 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1505 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1509 modest_msg_view_window_zoom_plus (ModestWindow *window)
1512 ModestMsgViewWindowPrivate *priv;
1516 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1517 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1519 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1521 if (zoom_level >= 2.0) {
1522 hildon_banner_show_information (NULL, NULL,
1523 _CS("ckct_ib_max_zoom_level_reached"));
1525 } else if (zoom_level >= 1.5) {
1527 } else if (zoom_level >= 1.2) {
1529 } else if (zoom_level >= 1.0) {
1531 } else if (zoom_level >= 0.8) {
1533 } else if (zoom_level >= 0.5) {
1539 /* set zoom level */
1540 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1541 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1542 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1543 g_free (banner_text);
1544 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1550 modest_msg_view_window_zoom_minus (ModestWindow *window)
1553 ModestMsgViewWindowPrivate *priv;
1557 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1558 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1560 zoom_level = modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1562 if (zoom_level <= 0.5) {
1563 hildon_banner_show_information (NULL, NULL,
1564 _CS("ckct_ib_min_zoom_level_reached"));
1566 } else if (zoom_level <= 0.8) {
1568 } else if (zoom_level <= 1.0) {
1570 } else if (zoom_level <= 1.2) {
1572 } else if (zoom_level <= 1.5) {
1574 } else if (zoom_level <= 2.0) {
1580 /* set zoom level */
1581 int_zoom = (gint) rint (zoom_level*100.0+0.1);
1582 banner_text = g_strdup_printf (_HL("wdgt_ib_zoom"), int_zoom);
1583 modest_platform_information_banner (GTK_WIDGET (window), NULL, banner_text);
1584 g_free (banner_text);
1585 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom_level);
1592 modest_msg_view_window_key_event (GtkWidget *window,
1598 focus = gtk_window_get_focus (GTK_WINDOW (window));
1600 /* for the find toolbar case */
1601 if (focus && GTK_IS_ENTRY (focus)) {
1602 if (event->keyval == GDK_BackSpace) {
1604 copy = gdk_event_copy ((GdkEvent *) event);
1605 gtk_widget_event (focus, copy);
1606 gdk_event_free (copy);
1611 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1612 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1613 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1614 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1615 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1616 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1617 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1618 /* gboolean return_value; */
1620 if (event->type == GDK_KEY_PRESS) {
1621 GtkScrollType scroll_type;
1623 switch (event->keyval) {
1626 scroll_type = GTK_SCROLL_STEP_UP; break;
1629 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1631 case GDK_KP_Page_Up:
1632 scroll_type = GTK_SCROLL_PAGE_UP; break;
1634 case GDK_KP_Page_Down:
1635 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1638 scroll_type = GTK_SCROLL_START; break;
1641 scroll_type = GTK_SCROLL_END; break;
1642 default: scroll_type = GTK_SCROLL_NONE;
1645 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1646 /* scroll_type, FALSE, &return_value); */
1657 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1660 ModestMsgViewWindowPrivate *priv;
1661 GtkTreeIter tmp_iter;
1662 gboolean is_last_selected;
1664 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1665 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1667 /*if no model (so no rows at all), then virtually we are the last*/
1668 if (!priv->header_model || !priv->row_reference)
1671 if (!gtk_tree_row_reference_valid (priv->row_reference))
1674 path = gtk_tree_row_reference_get_path (priv->row_reference);
1678 is_last_selected = TRUE;
1679 while (is_last_selected) {
1681 gtk_tree_path_next (path);
1682 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1684 gtk_tree_model_get (priv->header_model, &tmp_iter,
1685 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1688 if (msg_is_visible (header, priv->is_outbox))
1689 is_last_selected = FALSE;
1690 g_object_unref(G_OBJECT(header));
1693 gtk_tree_path_free (path);
1694 return is_last_selected;
1698 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1700 ModestMsgViewWindowPrivate *priv;
1702 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1703 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1705 return priv->header_model != NULL;
1709 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1711 ModestMsgViewWindowPrivate *priv;
1713 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1714 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1716 return priv->is_search_result;
1720 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1722 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1724 if (!check_outbox) {
1727 ModestTnySendQueueStatus status;
1728 status = modest_tny_all_send_queues_get_msg_status (header);
1729 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1730 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1735 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1738 ModestMsgViewWindowPrivate *priv;
1739 gboolean is_first_selected;
1740 GtkTreeIter tmp_iter;
1742 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1743 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1745 /*if no model (so no rows at all), then virtually we are the first*/
1746 if (!priv->header_model || !priv->row_reference)
1749 if (!gtk_tree_row_reference_valid (priv->row_reference))
1752 path = gtk_tree_row_reference_get_path (priv->row_reference);
1756 is_first_selected = TRUE;
1757 while (is_first_selected) {
1759 if(!gtk_tree_path_prev (path))
1761 /* Here the 'if' is needless for logic, but let make sure
1762 * iter is valid for gtk_tree_model_get. */
1763 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1765 gtk_tree_model_get (priv->header_model, &tmp_iter,
1766 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1769 if (msg_is_visible (header, priv->is_outbox))
1770 is_first_selected = FALSE;
1771 g_object_unref(G_OBJECT(header));
1774 gtk_tree_path_free (path);
1775 return is_first_selected;
1780 GtkTreeRowReference *row_reference;
1784 message_reader_performer (gboolean canceled,
1786 GtkWindow *parent_window,
1787 TnyAccount *account,
1790 ModestMailOperation *mail_op = NULL;
1791 MsgReaderInfo *info;
1793 info = (MsgReaderInfo *) user_data;
1794 if (canceled || err) {
1798 /* Register the header - it'll be unregistered in the callback */
1799 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1801 /* New mail operation */
1802 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1803 modest_ui_actions_disk_operations_error_handler,
1806 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1807 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1808 g_object_unref (mail_op);
1810 /* Update dimming rules */
1811 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1812 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1815 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1816 g_object_unref (info->header);
1817 g_slice_free (MsgReaderInfo, info);
1822 * Reads the message whose summary item is @header. It takes care of
1823 * several things, among others:
1825 * If the message was not previously downloaded then ask the user
1826 * before downloading. If there is no connection launch the connection
1827 * dialog. Update toolbar dimming rules.
1829 * Returns: TRUE if the mail operation was started, otherwise if the
1830 * user do not want to download the message, or if the user do not
1831 * want to connect, then the operation is not issued
1834 message_reader (ModestMsgViewWindow *window,
1835 ModestMsgViewWindowPrivate *priv,
1837 GtkTreeRowReference *row_reference)
1839 ModestWindowMgr *mgr;
1840 TnyAccount *account;
1842 MsgReaderInfo *info;
1844 g_return_val_if_fail (row_reference != NULL, FALSE);
1846 mgr = modest_runtime_get_window_mgr ();
1847 /* Msg download completed */
1848 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1850 /* We set the header from model while we're loading */
1851 tny_header_view_set_header (TNY_HEADER_VIEW (priv->msg_view), header);
1852 gtk_window_set_title (GTK_WINDOW (window), _CS("ckdg_pb_updating"));
1854 /* Ask the user if he wants to download the message if
1856 if (!tny_device_is_online (modest_runtime_get_device())) {
1857 GtkResponseType response;
1859 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1860 _("mcen_nc_get_msg"));
1861 if (response == GTK_RESPONSE_CANCEL)
1864 folder = tny_header_get_folder (header);
1865 info = g_slice_new (MsgReaderInfo);
1866 info->header = g_object_ref (header);
1867 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1869 /* Offer the connection dialog if necessary */
1870 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1872 TNY_FOLDER_STORE (folder),
1873 message_reader_performer,
1875 g_object_unref (folder);
1880 folder = tny_header_get_folder (header);
1881 account = tny_folder_get_account (folder);
1882 info = g_slice_new (MsgReaderInfo);
1883 info->header = g_object_ref (header);
1884 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1886 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1887 g_object_unref (account);
1888 g_object_unref (folder);
1894 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1896 ModestMsgViewWindowPrivate *priv;
1897 GtkTreePath *path= NULL;
1898 GtkTreeIter tmp_iter;
1900 gboolean retval = TRUE;
1901 GtkTreeRowReference *row_reference = NULL;
1903 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1904 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1906 if (!priv->row_reference)
1909 /* Update the next row reference if it's not valid. This could
1910 happen if for example the header which it was pointing to,
1911 was deleted. The best place to do it is in the row-deleted
1912 handler but the tinymail model do not work like the glib
1913 tree models and reports the deletion when the row is still
1915 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1916 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1917 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1918 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1921 if (priv->next_row_reference)
1922 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1926 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1928 gtk_tree_model_get_iter (priv->header_model,
1931 gtk_tree_path_free (path);
1933 gtk_tree_model_get (priv->header_model, &tmp_iter,
1934 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1937 /* Read the message & show it */
1938 if (!message_reader (window, priv, header, row_reference)) {
1941 gtk_tree_row_reference_free (row_reference);
1944 g_object_unref (header);
1950 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1952 ModestMsgViewWindowPrivate *priv = NULL;
1954 gboolean finished = FALSE;
1955 gboolean retval = FALSE;
1957 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1958 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1960 /* Return inmediatly if there is no header model */
1961 if (!priv->header_model || !priv->row_reference)
1964 path = gtk_tree_row_reference_get_path (priv->row_reference);
1965 while (!finished && gtk_tree_path_prev (path)) {
1969 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1970 gtk_tree_model_get (priv->header_model, &iter,
1971 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1975 if (msg_is_visible (header, priv->is_outbox)) {
1976 GtkTreeRowReference *row_reference;
1977 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1978 /* Read the message & show it */
1979 retval = message_reader (window, priv, header, row_reference);
1980 gtk_tree_row_reference_free (row_reference);
1984 g_object_unref (header);
1988 gtk_tree_path_free (path);
1993 view_msg_cb (ModestMailOperation *mail_op,
2000 ModestMsgViewWindow *self = NULL;
2001 ModestMsgViewWindowPrivate *priv = NULL;
2002 GtkTreeRowReference *row_reference = NULL;
2004 /* Unregister the header (it was registered before creating the mail operation) */
2005 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2007 row_reference = (GtkTreeRowReference *) user_data;
2009 gtk_tree_row_reference_free (row_reference);
2013 /* If there was any error */
2014 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2015 gtk_tree_row_reference_free (row_reference);
2019 /* Get the window */
2020 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2021 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2022 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2024 /* Update the row reference */
2025 if (priv->row_reference != NULL) {
2026 gtk_tree_row_reference_free (priv->row_reference);
2027 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2028 if (priv->next_row_reference != NULL) {
2029 gtk_tree_row_reference_free (priv->next_row_reference);
2031 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2032 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2035 /* Mark header as read */
2036 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2037 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2039 /* Set new message */
2040 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2041 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2042 modest_msg_view_window_update_priority (self);
2043 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2044 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2047 /* Set the new message uid of the window */
2048 if (priv->msg_uid) {
2049 g_free (priv->msg_uid);
2050 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2053 /* Notify the observers */
2054 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2055 0, priv->header_model, priv->row_reference);
2058 g_object_unref (self);
2059 gtk_tree_row_reference_free (row_reference);
2063 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2065 ModestMsgViewWindowPrivate *priv;
2067 TnyFolderType folder_type;
2069 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2071 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2073 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2077 folder = tny_msg_get_folder (msg);
2079 folder_type = modest_tny_folder_guess_folder_type (folder);
2080 g_object_unref (folder);
2082 g_object_unref (msg);
2090 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2092 ModestMsgViewWindowPrivate *priv;
2093 TnyHeader *header = NULL;
2094 TnyHeaderFlags flags = 0;
2096 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2098 if (priv->header_model && priv->row_reference) {
2100 GtkTreePath *path = NULL;
2102 path = gtk_tree_row_reference_get_path (priv->row_reference);
2103 g_return_if_fail (path != NULL);
2104 gtk_tree_model_get_iter (priv->header_model,
2106 gtk_tree_row_reference_get_path (priv->row_reference));
2108 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2110 gtk_tree_path_free (path);
2113 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2115 header = tny_msg_get_header (msg);
2116 g_object_unref (msg);
2121 flags = tny_header_get_flags (header);
2122 g_object_unref(G_OBJECT(header));
2125 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2130 toolbar_resize (ModestMsgViewWindow *self)
2132 ModestMsgViewWindowPrivate *priv = NULL;
2133 ModestWindowPrivate *parent_priv = NULL;
2135 gint static_button_size;
2136 ModestWindowMgr *mgr;
2138 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2139 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2140 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2142 mgr = modest_runtime_get_window_mgr ();
2143 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2145 if (parent_priv->toolbar) {
2146 /* left size buttons */
2147 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2148 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2149 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2150 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2151 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2152 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2153 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2154 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2155 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2156 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2157 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2158 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2159 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2160 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2161 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2162 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2164 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2165 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2166 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2167 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2173 modest_msg_view_window_show_toolbar (ModestWindow *self,
2174 gboolean show_toolbar)
2176 ModestMsgViewWindowPrivate *priv = NULL;
2177 ModestWindowPrivate *parent_priv;
2178 GtkWidget *reply_button = NULL, *menu = NULL;
2180 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2181 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2183 /* Set optimized view status */
2184 priv->optimized_view = !show_toolbar;
2186 if (!parent_priv->toolbar) {
2187 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2189 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2191 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2192 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2193 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2196 hildon_window_add_toolbar (HILDON_WINDOW (self),
2197 GTK_TOOLBAR (parent_priv->toolbar));
2199 /* Set reply button tap and hold menu */
2200 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2201 "/ToolBar/ToolbarMessageReply");
2202 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2203 "/ToolbarReplyCSM");
2204 if (menu && reply_button)
2205 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2209 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2210 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2211 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2213 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2214 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2215 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), TRUE);
2217 set_progress_hint (MODEST_MSG_VIEW_WINDOW (self), FALSE);
2220 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2221 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2226 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2228 ModestMsgViewWindow *window)
2230 if (!GTK_WIDGET_VISIBLE (window))
2233 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2237 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2239 ModestMsgViewWindowPrivate *priv;
2241 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2242 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2244 return priv->progress_hint;
2248 observers_empty (ModestMsgViewWindow *self)
2251 ModestMsgViewWindowPrivate *priv;
2252 gboolean is_empty = TRUE;
2253 guint pending_ops = 0;
2255 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2256 tmp = priv->progress_widgets;
2258 /* Check all observers */
2259 while (tmp && is_empty) {
2260 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2261 is_empty = pending_ops == 0;
2263 tmp = g_slist_next(tmp);
2270 on_account_removed (TnyAccountStore *account_store,
2271 TnyAccount *account,
2274 /* Do nothing if it's a transport account, because we only
2275 show the messages of a store account */
2276 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2277 const gchar *parent_acc = NULL;
2278 const gchar *our_acc = NULL;
2280 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2281 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2283 /* Close this window if I'm showing a message of the removed account */
2284 if (our_acc && parent_acc && strcmp (parent_acc, our_acc) == 0)
2285 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2290 on_mail_operation_started (ModestMailOperation *mail_op,
2293 ModestMsgViewWindow *self;
2294 ModestMailOperationTypeOperation op_type;
2296 ModestMsgViewWindowPrivate *priv;
2297 GObject *source = NULL;
2299 self = MODEST_MSG_VIEW_WINDOW (user_data);
2300 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2301 op_type = modest_mail_operation_get_type_operation (mail_op);
2302 tmp = priv->progress_widgets;
2303 source = modest_mail_operation_get_source(mail_op);
2304 if (G_OBJECT (self) == source) {
2305 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2306 set_toolbar_transfer_mode(self);
2308 modest_progress_object_add_operation (
2309 MODEST_PROGRESS_OBJECT (tmp->data),
2311 tmp = g_slist_next (tmp);
2315 g_object_unref (source);
2319 on_mail_operation_finished (ModestMailOperation *mail_op,
2322 ModestMsgViewWindow *self;
2323 ModestMailOperationTypeOperation op_type;
2325 ModestMsgViewWindowPrivate *priv;
2327 self = MODEST_MSG_VIEW_WINDOW (user_data);
2328 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2329 op_type = modest_mail_operation_get_type_operation (mail_op);
2330 tmp = priv->progress_widgets;
2332 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2334 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2336 tmp = g_slist_next (tmp);
2339 /* If no more operations are being observed, NORMAL mode is enabled again */
2340 if (observers_empty (self)) {
2341 set_progress_hint (self, FALSE);
2344 /* Update dimming rules. We have to do this right here
2345 and not in view_msg_cb because at that point the
2346 transfer mode is still enabled so the dimming rule
2347 won't let the user delete the message that has been
2348 readed for example */
2349 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2350 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2355 on_queue_changed (ModestMailOperationQueue *queue,
2356 ModestMailOperation *mail_op,
2357 ModestMailOperationQueueNotification type,
2358 ModestMsgViewWindow *self)
2360 ModestMsgViewWindowPrivate *priv;
2362 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2364 /* If this operations was created by another window, do nothing */
2365 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2368 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2369 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2371 "operation-started",
2372 G_CALLBACK (on_mail_operation_started),
2374 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2376 "operation-finished",
2377 G_CALLBACK (on_mail_operation_finished),
2379 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2380 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2382 "operation-started");
2383 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2385 "operation-finished");
2390 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2392 ModestMsgViewWindowPrivate *priv;
2393 TnyList *selected_attachments = NULL;
2395 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2396 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2398 /* In Hildon 2.2 as there's no selection we assume we have all attachments selected */
2399 selected_attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2401 return selected_attachments;
2407 guint banner_idle_id;
2408 } DecodeAsyncHelper;
2411 decode_async_banner_idle (gpointer user_data)
2413 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2415 helper->banner_idle_id = 0;
2416 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2422 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2428 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2430 if (helper->banner_idle_id > 0) {
2431 g_source_remove (helper->banner_idle_id);
2432 helper->banner_idle_id = 0;
2434 if (helper->banner) {
2435 gtk_widget_destroy (helper->banner);
2436 helper->banner = NULL;
2438 if (cancelled || err) {
2439 modest_platform_information_banner (NULL, NULL,
2440 _("mail_ib_file_operation_failed"));
2444 /* make the file read-only */
2445 g_chmod(helper->filepath, 0444);
2447 /* Activate the file */
2448 modest_platform_activate_file (helper->filepath, tny_mime_part_get_content_type (mime_part));
2452 g_free (helper->filepath);
2453 g_slice_free (DecodeAsyncHelper, helper);
2457 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2458 TnyMimePart *mime_part)
2460 ModestMsgViewWindowPrivate *priv;
2461 const gchar *msg_uid;
2462 gchar *attachment_uid = NULL;
2463 gint attachment_index = 0;
2464 TnyList *attachments;
2466 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2467 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2468 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2470 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2471 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2472 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2473 g_object_unref (attachments);
2475 if (msg_uid && attachment_index >= 0) {
2476 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2479 if (mime_part == NULL) {
2480 gboolean error = FALSE;
2481 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2482 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2484 } else if (tny_list_get_length (selected_attachments) > 1) {
2485 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2489 iter = tny_list_create_iterator (selected_attachments);
2490 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2491 g_object_unref (iter);
2493 g_object_unref (selected_attachments);
2498 g_object_ref (mime_part);
2501 if (tny_mime_part_is_purged (mime_part)) {
2502 g_object_unref (mime_part);
2506 if (!modest_tny_mime_part_is_msg (mime_part)) {
2507 gchar *filepath = NULL;
2508 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2509 gboolean show_error_banner = FALSE;
2510 TnyFsStream *temp_stream = NULL;
2511 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2514 if (temp_stream != NULL) {
2515 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2516 helper->filepath = g_strdup (filepath);
2517 helper->banner = NULL;
2518 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2519 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2520 on_decode_to_stream_async_handler,
2523 g_object_unref (temp_stream);
2524 /* NOTE: files in the temporary area will be automatically
2525 * cleaned after some time if they are no longer in use */
2528 const gchar *content_type;
2529 /* the file may already exist but it isn't writable,
2530 * let's try to open it anyway */
2531 content_type = tny_mime_part_get_content_type (mime_part);
2532 modest_platform_activate_file (filepath, content_type);
2534 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2535 show_error_banner = TRUE;
2540 if (show_error_banner)
2541 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2543 /* message attachment */
2544 TnyHeader *header = NULL;
2545 ModestWindowMgr *mgr;
2546 ModestWindow *msg_win = NULL;
2549 header = tny_msg_get_header (TNY_MSG (mime_part));
2550 mgr = modest_runtime_get_window_mgr ();
2551 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2554 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2555 * thus, we don't do anything */
2556 g_warning ("window for is already being created");
2558 /* it's not found, so create a new window for it */
2559 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2560 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2562 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2563 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2564 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2565 modest_window_get_zoom (MODEST_WINDOW (window)));
2566 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2567 gtk_widget_show_all (GTK_WIDGET (msg_win));
2570 g_object_unref (mime_part);
2583 GnomeVFSResult result;
2586 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2587 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2588 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2589 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2592 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2596 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2597 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2598 g_free (pair->filename);
2599 g_object_unref (pair->part);
2600 g_slice_free (SaveMimePartPair, pair);
2602 g_list_free (info->pairs);
2605 gtk_widget_destroy (info->banner);
2606 g_slice_free (SaveMimePartInfo, info);
2611 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2613 if (info->pairs != NULL) {
2614 save_mime_part_to_file (info);
2616 /* This is a GDK lock because we are an idle callback and
2617 * hildon_banner_show_information is or does Gtk+ code */
2619 gdk_threads_enter (); /* CHECKED */
2620 save_mime_part_info_free (info, TRUE);
2621 if (info->result == GNOME_VFS_OK) {
2622 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2623 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2624 hildon_banner_show_information (NULL, NULL,
2625 _KR("cerm_device_memory_full"));
2627 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2629 gdk_threads_leave (); /* CHECKED */
2636 save_mime_part_to_file (SaveMimePartInfo *info)
2638 GnomeVFSHandle *handle;
2640 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2642 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2643 if (info->result == GNOME_VFS_OK) {
2644 GError *error = NULL;
2645 stream = tny_vfs_stream_new (handle);
2646 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2647 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2649 if ((error->domain == TNY_ERROR_DOMAIN) &&
2650 (error->code = TNY_IO_ERROR_WRITE) &&
2651 (errno == ENOSPC)) {
2652 info->result = GNOME_VFS_ERROR_NO_SPACE;
2654 info->result = GNOME_VFS_ERROR_IO;
2657 g_object_unref (G_OBJECT (stream));
2658 g_object_unref (pair->part);
2659 g_slice_free (SaveMimePartPair, pair);
2660 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2662 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2663 save_mime_part_info_free (info, FALSE);
2666 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2671 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2673 gboolean is_ok = TRUE;
2674 gint replaced_files = 0;
2675 const GList *files = info->pairs;
2678 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2679 SaveMimePartPair *pair = iter->data;
2680 if (modest_utils_file_exists (pair->filename)) {
2684 if (replaced_files) {
2685 GtkWidget *confirm_overwrite_dialog;
2686 const gchar *message = (replaced_files == 1) ?
2687 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2688 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2689 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2692 gtk_widget_destroy (confirm_overwrite_dialog);
2696 save_mime_part_info_free (info, TRUE);
2698 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2699 _CS("sfil_ib_saving"));
2700 info->banner = banner;
2701 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2707 save_attachments_response (GtkDialog *dialog,
2711 TnyList *mime_parts;
2713 GList *files_to_save = NULL;
2715 mime_parts = TNY_LIST (user_data);
2717 if (arg1 != GTK_RESPONSE_OK)
2720 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2722 if (!modest_utils_folder_writable (chooser_uri)) {
2723 hildon_banner_show_information
2724 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2728 iter = tny_list_create_iterator (mime_parts);
2729 while (!tny_iterator_is_done (iter)) {
2730 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2732 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2733 !tny_mime_part_is_purged (mime_part) &&
2734 (tny_mime_part_get_filename (mime_part) != NULL)) {
2735 SaveMimePartPair *pair;
2737 pair = g_slice_new0 (SaveMimePartPair);
2739 if (tny_list_get_length (mime_parts) > 1) {
2741 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2742 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2745 pair->filename = g_strdup (chooser_uri);
2747 pair->part = mime_part;
2748 files_to_save = g_list_prepend (files_to_save, pair);
2750 tny_iterator_next (iter);
2752 g_object_unref (iter);
2754 g_free (chooser_uri);
2756 if (files_to_save != NULL) {
2757 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2758 info->pairs = files_to_save;
2759 info->result = TRUE;
2760 save_mime_parts_to_file_with_checks (info);
2764 /* Free and close the dialog */
2765 g_object_unref (mime_parts);
2766 gtk_widget_destroy (GTK_WIDGET (dialog));
2770 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2772 ModestMsgViewWindowPrivate *priv;
2773 GtkWidget *save_dialog = NULL;
2774 gchar *folder = NULL;
2775 gchar *filename = NULL;
2776 gchar *save_multiple_str = NULL;
2778 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2779 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2781 if (mime_parts == NULL) {
2782 /* In Hildon 2.2 save and delete operate over all the attachments as there's no
2783 * selection available */
2784 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2785 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2788 g_object_ref (mime_parts);
2791 /* prepare dialog */
2792 if (tny_list_get_length (mime_parts) == 1) {
2794 /* only one attachment selected */
2795 iter = tny_list_create_iterator (mime_parts);
2796 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2797 g_object_unref (iter);
2798 if (!modest_tny_mime_part_is_msg (mime_part) &&
2799 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2800 !tny_mime_part_is_purged (mime_part)) {
2801 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2803 /* TODO: show any error? */
2804 g_warning ("Tried to save a non-file attachment");
2805 g_object_unref (mime_parts);
2808 g_object_unref (mime_part);
2810 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2811 tny_list_get_length (mime_parts));
2814 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2815 GTK_FILE_CHOOSER_ACTION_SAVE);
2818 folder = g_build_filename (g_getenv (MYDOCS_ENV), DOCS_FOLDER, NULL);
2819 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2824 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2829 /* if multiple, set multiple string */
2830 if (save_multiple_str) {
2831 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2832 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2835 /* We must run this asynchronously, because the hildon dialog
2836 performs a gtk_dialog_run by itself which leads to gdk
2838 g_signal_connect (save_dialog, "response",
2839 G_CALLBACK (save_attachments_response), mime_parts);
2841 gtk_widget_show_all (save_dialog);
2845 show_remove_attachment_information (gpointer userdata)
2847 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2848 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2850 /* We're outside the main lock */
2851 gdk_threads_enter ();
2853 if (priv->remove_attachment_banner != NULL) {
2854 gtk_widget_destroy (priv->remove_attachment_banner);
2855 g_object_unref (priv->remove_attachment_banner);
2858 priv->remove_attachment_banner = g_object_ref (
2859 hildon_banner_show_animation (NULL, NULL, _("mcen_me_inbox_remove_attachments")));
2861 gdk_threads_leave ();
2867 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2869 ModestMsgViewWindowPrivate *priv;
2870 TnyList *mime_parts = NULL;
2871 gchar *confirmation_message;
2877 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2878 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2880 /* In hildon 2.2 we ignore the get_all flag as we always get all attachments. This is
2881 * because we don't have selection
2883 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2885 /* Remove already purged messages from mime parts list */
2886 iter = tny_list_create_iterator (mime_parts);
2887 while (!tny_iterator_is_done (iter)) {
2888 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2889 tny_iterator_next (iter);
2890 if (tny_mime_part_is_purged (part)) {
2891 tny_list_remove (mime_parts, (GObject *) part);
2893 g_object_unref (part);
2895 g_object_unref (iter);
2897 if (tny_list_get_length (mime_parts) == 0) {
2898 g_object_unref (mime_parts);
2902 n_attachments = tny_list_get_length (mime_parts);
2903 if (n_attachments == 1) {
2907 iter = tny_list_create_iterator (mime_parts);
2908 part = (TnyMimePart *) tny_iterator_get_current (iter);
2909 g_object_unref (iter);
2910 if (modest_tny_mime_part_is_msg (part)) {
2912 header = tny_msg_get_header (TNY_MSG (part));
2913 filename = tny_header_dup_subject (header);
2914 g_object_unref (header);
2915 if (filename == NULL)
2916 filename = g_strdup (_("mail_va_no_subject"));
2918 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2920 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2922 g_object_unref (part);
2924 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2925 "mcen_nc_purge_files_text",
2926 n_attachments), n_attachments);
2928 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2929 confirmation_message);
2930 g_free (confirmation_message);
2932 if (response != GTK_RESPONSE_OK) {
2933 g_object_unref (mime_parts);
2937 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2939 iter = tny_list_create_iterator (mime_parts);
2940 while (!tny_iterator_is_done (iter)) {
2943 part = (TnyMimePart *) tny_iterator_get_current (iter);
2944 tny_mime_part_set_purged (TNY_MIME_PART (part));
2945 g_object_unref (part);
2946 tny_iterator_next (iter);
2948 g_object_unref (iter);
2950 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2951 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2952 tny_msg_rewrite_cache (msg);
2953 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2954 g_object_unref (msg);
2956 g_object_unref (mime_parts);
2958 if (priv->purge_timeout > 0) {
2959 g_source_remove (priv->purge_timeout);
2960 priv->purge_timeout = 0;
2963 if (priv->remove_attachment_banner) {
2964 gtk_widget_destroy (priv->remove_attachment_banner);
2965 g_object_unref (priv->remove_attachment_banner);
2966 priv->remove_attachment_banner = NULL;
2974 update_window_title (ModestMsgViewWindow *window)
2976 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2978 TnyHeader *header = NULL;
2979 gchar *subject = NULL;
2981 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2984 header = tny_msg_get_header (msg);
2985 subject = tny_header_dup_subject (header);
2986 g_object_unref (header);
2987 g_object_unref (msg);
2990 if ((subject == NULL)||(subject[0] == '\0')) {
2992 subject = g_strdup (_("mail_va_no_subject"));
2995 gtk_window_set_title (GTK_WINDOW (window), subject);
2999 static void on_move_focus (GtkWidget *widget,
3000 GtkDirectionType direction,
3003 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3007 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3009 GnomeVFSResult result;
3010 GnomeVFSHandle *handle = NULL;
3011 GnomeVFSFileInfo *info = NULL;
3014 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3015 if (result != GNOME_VFS_OK) {
3020 info = gnome_vfs_file_info_new ();
3021 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3022 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3023 /* We put a "safe" default size for going to cache */
3024 *expected_size = (300*1024);
3026 *expected_size = info->size;
3028 gnome_vfs_file_info_unref (info);
3030 stream = tny_vfs_stream_new (handle);
3039 TnyStream *output_stream;
3040 GtkWidget *msg_view;
3044 on_fetch_image_idle_refresh_view (gpointer userdata)
3047 FetchImageData *fidata = (FetchImageData *) userdata;
3048 g_message ("REFRESH VIEW");
3049 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3050 g_message ("QUEUING DRAW");
3051 gtk_widget_queue_draw (fidata->msg_view);
3053 g_object_unref (fidata->msg_view);
3054 g_slice_free (FetchImageData, fidata);
3059 on_fetch_image_thread (gpointer userdata)
3061 FetchImageData *fidata = (FetchImageData *) userdata;
3062 TnyStreamCache *cache;
3063 TnyStream *cache_stream;
3065 cache = modest_runtime_get_images_cache ();
3066 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3067 g_free (fidata->cache_id);
3068 g_free (fidata->uri);
3070 if (cache_stream != NULL) {
3071 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3072 tny_stream_close (cache_stream);
3073 g_object_unref (cache_stream);
3076 tny_stream_close (fidata->output_stream);
3077 g_object_unref (fidata->output_stream);
3080 gdk_threads_enter ();
3081 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3082 gdk_threads_leave ();
3088 on_fetch_image (ModestMsgView *msgview,
3091 ModestMsgViewWindow *window)
3093 const gchar *current_account;
3094 ModestMsgViewWindowPrivate *priv;
3095 FetchImageData *fidata;
3097 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3099 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3101 fidata = g_slice_new0 (FetchImageData);
3102 fidata->msg_view = g_object_ref (msgview);
3103 fidata->uri = g_strdup (uri);
3104 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3105 fidata->output_stream = g_object_ref (stream);
3107 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3108 g_object_unref (fidata->output_stream);
3109 g_free (fidata->cache_id);
3110 g_free (fidata->uri);
3111 g_object_unref (fidata->msg_view);
3112 g_slice_free (FetchImageData, fidata);
3113 tny_stream_close (stream);
3121 setup_menu (ModestMsgViewWindow *self)
3123 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW(self));
3125 /* Settings menu buttons */
3126 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_replytoall"), NULL,
3127 APP_MENU_CALLBACK (modest_ui_actions_on_reply_all),
3128 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3129 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_forward"), "<Control>d",
3130 APP_MENU_CALLBACK (modest_ui_actions_on_forward),
3131 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_reply_msg));
3133 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_read"), NULL,
3134 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_read),
3135 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_read_msg_in_view));
3136 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_mark_as_unread"), NULL,
3137 APP_MENU_CALLBACK (modest_ui_actions_on_mark_as_unread),
3138 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_mark_as_unread_msg_in_view));
3140 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_save_attachments"), NULL,
3141 APP_MENU_CALLBACK (modest_ui_actions_save_attachments),
3142 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_save_attachments));
3143 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_inbox_remove_attachments"), NULL,
3144 APP_MENU_CALLBACK (modest_ui_actions_remove_attachments),
3145 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_remove_attachments));
3147 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_new_message"), "<Control>n",
3148 APP_MENU_CALLBACK (modest_ui_actions_on_new_msg),
3149 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_new_msg));
3150 modest_hildon2_window_add_to_menu (MODEST_HILDON2_WINDOW (self), _("mcen_me_viewer_addtocontacts"), NULL,
3151 APP_MENU_CALLBACK (modest_ui_actions_add_to_contacts),
3152 MODEST_DIMMING_CALLBACK (modest_ui_dimming_rules_on_add_to_contacts));
3156 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3158 ModestMsgViewWindowPrivate *priv;
3159 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
3160 GSList *recipients = NULL;
3162 gboolean contacts_to_add = FALSE;
3164 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3168 header = modest_msg_view_window_get_header (self);
3171 recipients = modest_tny_msg_header_get_all_recipients_list (header);
3172 g_object_unref (header);
3174 recipients = modest_tny_msg_get_all_recipients_list (msg);
3175 g_object_unref (msg);
3178 if (recipients != NULL) {
3179 GtkWidget *picker_dialog;
3180 GtkWidget *selector;
3182 gchar *selected = NULL;
3184 selector = hildon_touch_selector_new_text ();
3185 g_object_ref (selector);
3187 for (node = recipients; node != NULL; node = g_slist_next (node)) {
3188 if (!modest_address_book_has_address ((const gchar *) node->data)) {
3189 hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector),
3190 (const gchar *) node->data);
3191 contacts_to_add = TRUE;
3195 if (contacts_to_add) {
3198 picker_dialog = hildon_picker_dialog_new (GTK_WINDOW (self));
3199 gtk_window_set_title (GTK_WINDOW (picker_dialog), _("mcen_me_viewer_addtocontacts"));
3201 hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (picker_dialog),
3202 HILDON_TOUCH_SELECTOR (selector));
3204 picker_result = gtk_dialog_run (GTK_DIALOG (picker_dialog));
3206 if (picker_result == GTK_RESPONSE_OK) {
3207 selected = hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector));
3209 gtk_widget_destroy (picker_dialog);
3212 modest_address_book_add_address (selected);
3217 g_object_unref (selector);
3222 if (recipients) {g_slist_foreach (recipients, (GFunc) g_free, NULL); g_slist_free (recipients);}