1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <glib/gi18n.h>
31 #include <tny-account-store.h>
32 #include <tny-simple-list.h>
34 #include <tny-mime-part.h>
35 #include <tny-vfs-stream.h>
36 #include "modest-marshal.h"
37 #include "modest-platform.h"
38 #include <modest-utils.h>
39 #include <modest-maemo-utils.h>
40 #include <modest-tny-msg.h>
41 #include <modest-msg-view-window.h>
42 #include <modest-main-window-ui.h>
43 #include "modest-msg-view-window-ui-dimming.h"
44 #include <modest-widget-memory.h>
45 #include <modest-runtime.h>
46 #include <modest-window-priv.h>
47 #include <modest-tny-folder.h>
48 #include <modest-text-utils.h>
49 #include <modest-account-mgr-helpers.h>
50 #include "modest-progress-bar.h"
51 #include "modest-defs.h"
52 #include "modest-hildon-includes.h"
53 #include "modest-ui-dimming-manager.h"
54 #include <gdk/gdkkeysyms.h>
55 #include <modest-tny-account.h>
56 #include <modest-mime-part-view.h>
57 #include <modest-isearch-view.h>
58 #include <modest-tny-mime-part.h>
61 #include <glib/gstdio.h>
62 #include <modest-debug.h>
64 #define DEFAULT_FOLDER "MyDocs/.documents"
66 static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass);
67 static void modest_msg_view_window_init (ModestMsgViewWindow *obj);
68 static void modest_header_view_observer_init(
69 ModestHeaderViewObserverIface *iface_class);
70 static void modest_msg_view_window_finalize (GObject *obj);
71 static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj,
73 static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
74 ModestMsgViewWindow *obj);
75 static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
76 ModestMsgViewWindow *obj);
78 static void modest_msg_view_window_disconnect_signals (ModestWindow *self);
79 static void modest_msg_view_window_set_zoom (ModestWindow *window,
81 static gdouble modest_msg_view_window_get_zoom (ModestWindow *window);
82 static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window);
83 static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window);
84 static gboolean modest_msg_view_window_key_event (GtkWidget *window,
87 static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget,
88 GdkEventWindowState *event,
90 static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window);
92 static void modest_msg_view_window_show_toolbar (ModestWindow *window,
93 gboolean show_toolbar);
95 static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
97 ModestMsgViewWindow *window);
99 static void modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
102 ModestMsgViewWindow *window);
104 static void modest_msg_view_window_on_row_deleted (GtkTreeModel *header_model,
106 ModestMsgViewWindow *window);
108 static void modest_msg_view_window_on_row_inserted (GtkTreeModel *header_model,
109 GtkTreePath *tree_path,
110 GtkTreeIter *tree_iter,
111 ModestMsgViewWindow *window);
113 static void modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
117 ModestMsgViewWindow *window);
119 static void modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *window,
121 const gchar *tny_folder_id);
123 static void cancel_progressbar (GtkToolButton *toolbutton,
124 ModestMsgViewWindow *self);
126 static void on_queue_changed (ModestMailOperationQueue *queue,
127 ModestMailOperation *mail_op,
128 ModestMailOperationQueueNotification type,
129 ModestMsgViewWindow *self);
131 static void on_account_removed (TnyAccountStore *account_store,
135 static void on_move_focus (GtkWidget *widget,
136 GtkDirectionType direction,
139 static void view_msg_cb (ModestMailOperation *mail_op,
146 static void set_toolbar_mode (ModestMsgViewWindow *self,
147 ModestToolBarModes mode);
149 static void update_window_title (ModestMsgViewWindow *window);
151 static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self);
152 static void init_window (ModestMsgViewWindow *obj);
154 static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox);
156 static void check_dimming_rules_after_change (ModestMsgViewWindow *window);
158 static gboolean on_fetch_image (ModestMsgView *msgview,
161 ModestMsgViewWindow *window);
163 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
164 GtkScrollType scroll_type,
168 /* list my signals */
175 static const GtkToggleActionEntry msg_view_toggle_action_entries [] = {
176 { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
177 { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "<CTRL>F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE },
180 static const GtkRadioActionEntry msg_view_zoom_action_entries [] = {
181 { "Zoom50", NULL, N_("mcen_me_viewer_50"), NULL, NULL, 50 },
182 { "Zoom80", NULL, N_("mcen_me_viewer_80"), NULL, NULL, 80 },
183 { "Zoom100", NULL, N_("mcen_me_viewer_100"), NULL, NULL, 100 },
184 { "Zoom120", NULL, N_("mcen_me_viewer_120"), NULL, NULL, 120 },
185 { "Zoom150", NULL, N_("mcen_me_viewer_150"), NULL, NULL, 150 },
186 { "Zoom200", NULL, N_("mcen_me_viewer_200"), NULL, NULL, 200 }
189 typedef struct _ModestMsgViewWindowPrivate ModestMsgViewWindowPrivate;
190 struct _ModestMsgViewWindowPrivate {
193 GtkWidget *main_scroll;
194 GtkWidget *find_toolbar;
197 /* Progress observers */
198 GtkWidget *progress_bar;
199 GSList *progress_widgets;
202 GtkWidget *progress_toolitem;
203 GtkWidget *cancel_toolitem;
204 GtkWidget *prev_toolitem;
205 GtkWidget *next_toolitem;
206 ModestToolBarModes current_toolbar_mode;
208 /* Optimized view enabled */
209 gboolean optimized_view;
211 /* Whether this was created via the *_new_for_search_result() function. */
212 gboolean is_search_result;
214 /* Whether the message is in outbox */
217 /* A reference to the @model of the header view
218 * to allow selecting previous/next messages,
219 * if the message is currently selected in the header view.
221 const gchar *header_folder_id;
222 GtkTreeModel *header_model;
223 GtkTreeRowReference *row_reference;
224 GtkTreeRowReference *next_row_reference;
226 gulong clipboard_change_handler;
227 gulong queue_change_handler;
228 gulong account_removed_handler;
229 gulong row_changed_handler;
230 gulong row_deleted_handler;
231 gulong row_inserted_handler;
232 gulong rows_reordered_handler;
235 GtkWidget *remove_attachment_banner;
237 guint progress_bar_timeout;
244 #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
245 MODEST_TYPE_MSG_VIEW_WINDOW, \
246 ModestMsgViewWindowPrivate))
248 static GtkWindowClass *parent_class = NULL;
250 /* uncomment the following if you have defined any signals */
251 static guint signals[LAST_SIGNAL] = {0};
254 modest_msg_view_window_get_type (void)
256 static GType my_type = 0;
258 static const GTypeInfo my_info = {
259 sizeof(ModestMsgViewWindowClass),
260 NULL, /* base init */
261 NULL, /* base finalize */
262 (GClassInitFunc) modest_msg_view_window_class_init,
263 NULL, /* class finalize */
264 NULL, /* class data */
265 sizeof(ModestMsgViewWindow),
267 (GInstanceInitFunc) modest_msg_view_window_init,
270 my_type = g_type_register_static (MODEST_TYPE_WINDOW,
271 "ModestMsgViewWindow",
274 static const GInterfaceInfo modest_header_view_observer_info =
276 (GInterfaceInitFunc) modest_header_view_observer_init,
277 NULL, /* interface_finalize */
278 NULL /* interface_data */
281 g_type_add_interface_static (my_type,
282 MODEST_TYPE_HEADER_VIEW_OBSERVER,
283 &modest_header_view_observer_info);
289 save_state (ModestWindow *self)
291 modest_widget_memory_save (modest_runtime_get_conf (),
293 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
298 restore_settings (ModestMsgViewWindow *self)
301 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self);
304 conf = modest_runtime_get_conf ();
305 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
306 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu");
307 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
308 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL));
309 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
310 "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu");
311 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
312 modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL));
313 modest_widget_memory_restore (conf,
315 MODEST_CONF_MSG_VIEW_WINDOW_KEY);
318 static gboolean modest_msg_view_window_scroll_child (ModestMsgViewWindow *self,
319 GtkScrollType scroll_type,
323 ModestMsgViewWindowPrivate *priv;
324 gboolean return_value;
326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
327 g_signal_emit_by_name (priv->main_scroll, "scroll-child", scroll_type, horizontal, &return_value);
332 add_scroll_binding (GtkBindingSet *binding_set,
334 GtkScrollType scroll)
336 guint keypad_keyval = keyval - GDK_Left + GDK_KP_Left;
338 gtk_binding_entry_add_signal (binding_set, keyval, 0,
340 GTK_TYPE_SCROLL_TYPE, scroll,
341 G_TYPE_BOOLEAN, FALSE);
342 gtk_binding_entry_add_signal (binding_set, keypad_keyval, 0,
344 GTK_TYPE_SCROLL_TYPE, scroll,
345 G_TYPE_BOOLEAN, FALSE);
349 modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass)
351 GObjectClass *gobject_class;
352 ModestWindowClass *modest_window_class;
353 GtkBindingSet *binding_set;
355 gobject_class = (GObjectClass*) klass;
356 modest_window_class = (ModestWindowClass *) klass;
358 parent_class = g_type_class_peek_parent (klass);
359 gobject_class->finalize = modest_msg_view_window_finalize;
361 modest_window_class->set_zoom_func = modest_msg_view_window_set_zoom;
362 modest_window_class->get_zoom_func = modest_msg_view_window_get_zoom;
363 modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus;
364 modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus;
365 modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar;
366 modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals;
368 modest_window_class->save_state_func = save_state;
370 klass->scroll_child = modest_msg_view_window_scroll_child;
372 signals[MSG_CHANGED_SIGNAL] =
373 g_signal_new ("msg-changed",
374 G_TYPE_FROM_CLASS (gobject_class),
376 G_STRUCT_OFFSET (ModestMsgViewWindowClass, msg_changed),
378 modest_marshal_VOID__POINTER_POINTER,
379 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
381 signals[SCROLL_CHILD_SIGNAL] =
382 g_signal_new ("scroll-child",
383 G_TYPE_FROM_CLASS (gobject_class),
384 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
385 G_STRUCT_OFFSET (ModestMsgViewWindowClass, scroll_child),
387 modest_marshal_BOOLEAN__ENUM_BOOLEAN,
388 G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_BOOLEAN);
390 binding_set = gtk_binding_set_by_class (klass);
391 add_scroll_binding (binding_set, GDK_Up, GTK_SCROLL_STEP_UP);
392 add_scroll_binding (binding_set, GDK_Down, GTK_SCROLL_STEP_DOWN);
393 add_scroll_binding (binding_set, GDK_Page_Up, GTK_SCROLL_PAGE_UP);
394 add_scroll_binding (binding_set, GDK_Page_Down, GTK_SCROLL_PAGE_DOWN);
395 add_scroll_binding (binding_set, GDK_Home, GTK_SCROLL_START);
396 add_scroll_binding (binding_set, GDK_End, GTK_SCROLL_END);
398 g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate));
402 static void modest_header_view_observer_init(
403 ModestHeaderViewObserverIface *iface_class)
405 iface_class->update_func = modest_msg_view_window_update_model_replaced;
409 modest_msg_view_window_init (ModestMsgViewWindow *obj)
411 ModestMsgViewWindowPrivate *priv;
412 ModestWindowPrivate *parent_priv = NULL;
413 GtkActionGroup *action_group = NULL;
414 GError *error = NULL;
415 GdkPixbuf *window_icon;
417 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
418 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
419 parent_priv->ui_manager = gtk_ui_manager_new();
421 action_group = gtk_action_group_new ("ModestMsgViewWindowActions");
422 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
424 /* Add common actions */
425 gtk_action_group_add_actions (action_group,
426 modest_action_entries,
427 G_N_ELEMENTS (modest_action_entries),
429 gtk_action_group_add_toggle_actions (action_group,
430 modest_toggle_action_entries,
431 G_N_ELEMENTS (modest_toggle_action_entries),
433 gtk_action_group_add_toggle_actions (action_group,
434 msg_view_toggle_action_entries,
435 G_N_ELEMENTS (msg_view_toggle_action_entries),
437 gtk_action_group_add_radio_actions (action_group,
438 msg_view_zoom_action_entries,
439 G_N_ELEMENTS (msg_view_zoom_action_entries),
441 G_CALLBACK (modest_ui_actions_on_change_zoom),
444 gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0);
445 g_object_unref (action_group);
447 /* Load the UI definition */
448 gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml",
451 g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message);
452 g_error_free (error);
457 /* Add accelerators */
458 gtk_window_add_accel_group (GTK_WINDOW (obj),
459 gtk_ui_manager_get_accel_group (parent_priv->ui_manager));
461 priv->is_search_result = FALSE;
462 priv->is_outbox = FALSE;
464 priv->msg_view = NULL;
465 priv->header_model = NULL;
466 priv->header_folder_id = NULL;
467 priv->clipboard_change_handler = 0;
468 priv->queue_change_handler = 0;
469 priv->account_removed_handler = 0;
470 priv->row_changed_handler = 0;
471 priv->row_deleted_handler = 0;
472 priv->row_inserted_handler = 0;
473 priv->rows_reordered_handler = 0;
474 priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL;
476 priv->optimized_view = FALSE;
477 priv->progress_bar_timeout = 0;
478 priv->purge_timeout = 0;
479 priv->remove_attachment_banner = NULL;
480 priv->msg_uid = NULL;
482 priv->sighandlers = NULL;
485 init_window (MODEST_MSG_VIEW_WINDOW(obj));
487 /* Set window icon */
488 window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG);
490 gtk_window_set_icon (GTK_WINDOW (obj), window_icon);
491 g_object_unref (window_icon);
494 hildon_program_add_window (hildon_program_get_instance(),
497 modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(),
498 GTK_WINDOW(obj),"applications_email_viewer");
503 set_toolbar_transfer_mode (ModestMsgViewWindow *self)
505 ModestMsgViewWindowPrivate *priv = NULL;
507 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
509 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
511 set_toolbar_mode (self, TOOLBAR_MODE_TRANSFER);
513 if (priv->progress_bar_timeout > 0) {
514 g_source_remove (priv->progress_bar_timeout);
515 priv->progress_bar_timeout = 0;
522 set_toolbar_mode (ModestMsgViewWindow *self,
523 ModestToolBarModes mode)
525 ModestWindowPrivate *parent_priv;
526 ModestMsgViewWindowPrivate *priv;
527 /* GtkWidget *widget = NULL; */
529 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
531 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
532 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
534 /* Sets current toolbar mode */
535 priv->current_toolbar_mode = mode;
537 /* Update toolbar dimming state */
538 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
541 case TOOLBAR_MODE_NORMAL:
542 if (priv->progress_toolitem) {
543 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
544 gtk_widget_hide (priv->progress_toolitem);
547 if (priv->progress_bar)
548 gtk_widget_hide (priv->progress_bar);
550 if (priv->cancel_toolitem)
551 gtk_widget_hide (priv->cancel_toolitem);
553 if (priv->prev_toolitem)
554 gtk_widget_show (priv->prev_toolitem);
556 if (priv->next_toolitem)
557 gtk_widget_show (priv->next_toolitem);
559 /* Hide toolbar if optimized view is enabled */
560 if (priv->optimized_view) {
561 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
562 gtk_widget_hide (GTK_WIDGET(parent_priv->toolbar));
566 case TOOLBAR_MODE_TRANSFER:
567 if (priv->prev_toolitem)
568 gtk_widget_hide (priv->prev_toolitem);
570 if (priv->next_toolitem)
571 gtk_widget_hide (priv->next_toolitem);
573 if (priv->progress_bar)
574 gtk_widget_show (priv->progress_bar);
576 if (priv->progress_toolitem) {
577 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
578 gtk_widget_show (priv->progress_toolitem);
581 if (priv->cancel_toolitem)
582 gtk_widget_show (priv->cancel_toolitem);
584 /* Show toolbar if it's hiden (optimized view ) */
585 if (priv->optimized_view) {
586 gtk_widget_set_no_show_all (parent_priv->toolbar, FALSE);
587 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
592 g_return_if_reached ();
599 init_window (ModestMsgViewWindow *obj)
601 GtkWidget *main_vbox;
602 ModestMsgViewWindowPrivate *priv;
604 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
606 priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ()));
607 modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE);
608 main_vbox = gtk_vbox_new (FALSE, 6);
610 #ifdef MODEST_USE_MOZEMBED
611 priv->main_scroll = priv->msg_view;
612 gtk_widget_set_size_request (priv->msg_view, -1, 1600);
614 priv->main_scroll = gtk_scrolled_window_new (NULL, NULL);
615 gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view);
617 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
618 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE);
619 modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE);
621 gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0);
622 gtk_container_add (GTK_CONTAINER(obj), main_vbox);
624 priv->find_toolbar = hildon_find_toolbar_new (NULL);
625 hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar));
626 gtk_widget_set_no_show_all (priv->find_toolbar, TRUE);
628 gtk_widget_show_all (GTK_WIDGET(main_vbox));
632 modest_msg_view_window_disconnect_signals (ModestWindow *self)
634 ModestMsgViewWindowPrivate *priv;
635 ModestHeaderView *header_view = NULL;
636 ModestWindow *main_window = NULL;
638 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
640 if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) &&
641 g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
642 priv->clipboard_change_handler))
643 g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
644 priv->clipboard_change_handler);
646 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
647 priv->queue_change_handler))
648 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
649 priv->queue_change_handler);
651 if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()),
652 priv->account_removed_handler))
653 g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()),
654 priv->account_removed_handler);
656 if (priv->header_model) {
657 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
658 priv->row_changed_handler))
659 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
660 priv->row_changed_handler);
662 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
663 priv->row_deleted_handler))
664 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
665 priv->row_deleted_handler);
667 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
668 priv->row_inserted_handler))
669 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
670 priv->row_inserted_handler);
672 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
673 priv->rows_reordered_handler))
674 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
675 priv->rows_reordered_handler);
678 modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
679 priv->sighandlers = NULL;
681 main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(),
682 FALSE); /* don't create */
686 header_view = MODEST_HEADER_VIEW(
687 modest_main_window_get_child_widget(
688 MODEST_MAIN_WINDOW(main_window),
689 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
690 if (header_view == NULL)
693 modest_header_view_remove_observer(header_view,
694 MODEST_HEADER_VIEW_OBSERVER(self));
698 modest_msg_view_window_finalize (GObject *obj)
700 ModestMsgViewWindowPrivate *priv;
702 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
704 /* Sanity check: shouldn't be needed, the window mgr should
705 call this function before */
706 modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj));
708 if (priv->header_model != NULL) {
709 g_object_unref (priv->header_model);
710 priv->header_model = NULL;
713 if (priv->progress_bar_timeout > 0) {
714 g_source_remove (priv->progress_bar_timeout);
715 priv->progress_bar_timeout = 0;
718 if (priv->remove_attachment_banner) {
719 gtk_widget_destroy (priv->remove_attachment_banner);
720 g_object_unref (priv->remove_attachment_banner);
721 priv->remove_attachment_banner = NULL;
724 if (priv->purge_timeout > 0) {
725 g_source_remove (priv->purge_timeout);
726 priv->purge_timeout = 0;
729 if (priv->row_reference) {
730 gtk_tree_row_reference_free (priv->row_reference);
731 priv->row_reference = NULL;
734 if (priv->next_row_reference) {
735 gtk_tree_row_reference_free (priv->next_row_reference);
736 priv->next_row_reference = NULL;
740 g_free (priv->msg_uid);
741 priv->msg_uid = NULL;
744 G_OBJECT_CLASS(parent_class)->finalize (obj);
748 select_next_valid_row (GtkTreeModel *model,
749 GtkTreeRowReference **row_reference,
753 GtkTreeIter tmp_iter;
755 GtkTreePath *next = NULL;
756 gboolean retval = FALSE, finished;
758 g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE);
760 path = gtk_tree_row_reference_get_path (*row_reference);
761 gtk_tree_model_get_iter (model, &tmp_iter, path);
762 gtk_tree_row_reference_free (*row_reference);
763 *row_reference = NULL;
767 TnyHeader *header = NULL;
769 if (gtk_tree_model_iter_next (model, &tmp_iter)) {
770 gtk_tree_model_get (model, &tmp_iter,
771 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
775 if (msg_is_visible (header, is_outbox)) {
776 next = gtk_tree_model_get_path (model, &tmp_iter);
777 *row_reference = gtk_tree_row_reference_new (model, next);
778 gtk_tree_path_free (next);
782 g_object_unref (header);
785 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
786 next = gtk_tree_model_get_path (model, &tmp_iter);
788 /* Ensure that we are not selecting the same */
789 if (gtk_tree_path_compare (path, next) != 0) {
790 gtk_tree_model_get (model, &tmp_iter,
791 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
794 if (msg_is_visible (header, is_outbox)) {
795 *row_reference = gtk_tree_row_reference_new (model, next);
799 g_object_unref (header);
803 /* If we ended up in the same message
804 then there is no valid next
808 gtk_tree_path_free (next);
810 /* If there are no more messages and we don't
811 want to start again in the first one then
812 there is no valid next message */
818 gtk_tree_path_free (path);
823 /* TODO: This should be in _init(), with the parameters as properties. */
825 modest_msg_view_window_construct (ModestMsgViewWindow *self,
826 const gchar *modest_account_name,
827 const gchar *msg_uid)
830 ModestMsgViewWindowPrivate *priv = NULL;
831 ModestWindowPrivate *parent_priv = NULL;
832 ModestDimmingRulesGroup *menu_rules_group = NULL;
833 ModestDimmingRulesGroup *toolbar_rules_group = NULL;
834 ModestDimmingRulesGroup *clipboard_rules_group = NULL;
836 obj = G_OBJECT (self);
837 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj);
838 parent_priv = MODEST_WINDOW_GET_PRIVATE(obj);
840 priv->msg_uid = g_strdup (msg_uid);
843 parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar");
844 hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar));
845 gtk_widget_show (parent_priv->menubar);
846 parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new();
848 menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE);
849 toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE);
850 clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE);
852 /* Add common dimming rules */
853 modest_dimming_rules_group_add_rules (menu_rules_group,
854 modest_msg_view_menu_dimming_entries,
855 G_N_ELEMENTS (modest_msg_view_menu_dimming_entries),
856 MODEST_WINDOW (self));
857 modest_dimming_rules_group_add_rules (toolbar_rules_group,
858 modest_msg_view_toolbar_dimming_entries,
859 G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries),
860 MODEST_WINDOW (self));
861 modest_dimming_rules_group_add_rules (clipboard_rules_group,
862 modest_msg_view_clipboard_dimming_entries,
863 G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries),
864 MODEST_WINDOW (self));
866 /* Insert dimming rules group for this window */
867 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group);
868 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group);
869 modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group);
870 g_object_unref (menu_rules_group);
871 g_object_unref (toolbar_rules_group);
872 g_object_unref (clipboard_rules_group);
874 restore_settings (MODEST_MSG_VIEW_WINDOW(obj));
876 /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */
878 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);
879 g_signal_connect (G_OBJECT(priv->msg_view), "activate_link",
880 G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj);
881 g_signal_connect (G_OBJECT(priv->msg_view), "link_hover",
882 G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj);
883 g_signal_connect (G_OBJECT(priv->msg_view), "attachment_clicked",
884 G_CALLBACK (modest_ui_actions_on_msg_attachment_clicked), obj);
885 g_signal_connect (G_OBJECT(priv->msg_view), "recpt_activated",
886 G_CALLBACK (modest_ui_actions_on_msg_recpt_activated), obj);
887 g_signal_connect (G_OBJECT(priv->msg_view), "link_contextual",
888 G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj);
889 g_signal_connect (G_OBJECT (priv->msg_view), "fetch_image",
890 G_CALLBACK (on_fetch_image), obj);
892 g_signal_connect (G_OBJECT (obj), "key-release-event",
893 G_CALLBACK (modest_msg_view_window_key_event),
896 g_signal_connect (G_OBJECT (obj), "key-press-event",
897 G_CALLBACK (modest_msg_view_window_key_event),
900 g_signal_connect (G_OBJECT (obj), "window-state-event",
901 G_CALLBACK (modest_msg_view_window_window_state_event),
904 g_signal_connect (G_OBJECT (obj), "move-focus",
905 G_CALLBACK (on_move_focus), obj);
907 /* Mail Operation Queue */
908 priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()),
910 G_CALLBACK (on_queue_changed),
913 /* Account manager */
914 priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()),
916 G_CALLBACK(on_account_removed),
919 modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name);
921 g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj);
922 g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj);
923 priv->last_search = NULL;
925 /* Init the clipboard actions dim status */
926 modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view));
928 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
933 /* FIXME: parameter checks */
935 modest_msg_view_window_new_with_header_model (TnyMsg *msg,
936 const gchar *modest_account_name,
937 const gchar *mailbox, /*ignored */
938 const gchar *msg_uid,
940 GtkTreeRowReference *row_reference)
942 ModestMsgViewWindow *window = NULL;
943 ModestMsgViewWindowPrivate *priv = NULL;
944 TnyFolder *header_folder = NULL;
945 ModestHeaderView *header_view = NULL;
946 ModestWindow *main_window = NULL;
947 ModestWindowMgr *mgr = NULL;
950 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
953 mgr = modest_runtime_get_window_mgr ();
954 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
955 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
957 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
959 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
961 /* Remember the message list's TreeModel so we can detect changes
962 * and change the list selection when necessary: */
964 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
966 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
967 MODEST_MAIN_WINDOW(main_window),
968 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
971 if (header_view != NULL){
972 header_folder = modest_header_view_get_folder(header_view);
973 /* This could happen if the header folder was
974 unseleted before opening this msg window (for
975 example if the user selects an account in the
976 folder view of the main window */
978 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
979 priv->header_folder_id = tny_folder_get_id(header_folder);
980 g_assert(priv->header_folder_id != NULL);
981 g_object_unref(header_folder);
985 /* Setup row references and connect signals */
986 priv->header_model = g_object_ref (model);
989 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
990 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
991 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
993 priv->row_reference = NULL;
994 priv->next_row_reference = NULL;
997 /* Connect signals */
998 priv->row_changed_handler =
999 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
1000 G_CALLBACK(modest_msg_view_window_on_row_changed),
1002 priv->row_deleted_handler =
1003 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
1004 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1006 priv->row_inserted_handler =
1007 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
1008 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1010 priv->rows_reordered_handler =
1011 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
1012 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1015 if (header_view != NULL){
1016 modest_header_view_add_observer(header_view,
1017 MODEST_HEADER_VIEW_OBSERVER(window));
1020 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1021 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1022 gtk_widget_show_all (GTK_WIDGET (window));
1023 modest_msg_view_window_update_priority (window);
1025 /* Check dimming rules */
1026 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1027 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1028 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1030 return MODEST_WINDOW(window);
1034 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1035 const gchar *modest_account_name,
1036 const gchar *mailbox, /*ignored*/
1037 const gchar *msg_uid)
1039 ModestMsgViewWindow *window = NULL;
1040 ModestMsgViewWindowPrivate *priv = NULL;
1041 ModestWindowMgr *mgr = NULL;
1043 mgr = modest_runtime_get_window_mgr ();
1044 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1045 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1046 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1048 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1050 /* Remember that this is a search result,
1051 * so we can disable some UI appropriately: */
1052 priv->is_search_result = TRUE;
1054 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1056 update_window_title (window);
1057 gtk_widget_show_all (GTK_WIDGET (window));
1058 modest_msg_view_window_update_priority (window);
1060 /* Check dimming rules */
1061 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1062 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1063 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1065 return MODEST_WINDOW(window);
1069 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1070 const gchar *modest_account_name,
1071 const gchar *mailbox, /* ignored */
1072 const gchar *msg_uid)
1074 GObject *obj = NULL;
1075 ModestMsgViewWindowPrivate *priv;
1076 ModestWindowMgr *mgr = NULL;
1078 g_return_val_if_fail (msg, NULL);
1079 mgr = modest_runtime_get_window_mgr ();
1080 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1081 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1082 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1083 modest_account_name, msg_uid);
1085 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1086 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1088 gtk_widget_show_all (GTK_WIDGET (obj));
1090 /* Check dimming rules */
1091 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1092 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1093 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1095 return MODEST_WINDOW(obj);
1099 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1102 ModestMsgViewWindow *window)
1104 check_dimming_rules_after_change (window);
1108 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1110 ModestMsgViewWindow *window)
1112 check_dimming_rules_after_change (window);
1114 /* The window could have dissapeared */
1117 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1119 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1120 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1124 /* On insertions we check if the folder still has the message we are
1125 * showing or do not. If do not, we do nothing. Which means we are still
1126 * not attached to any header folder and thus next/prev buttons are
1127 * still dimmed. Once the message that is shown by msg-view is found, the
1128 * new model of header-view will be attached and the references will be set.
1129 * On each further insertions dimming rules will be checked. However
1130 * this requires extra CPU time at least works.
1131 * (An message might be deleted from TnyFolder and thus will not be
1132 * inserted into the model again for example if it is removed by the
1133 * imap server and the header view is refreshed.)
1136 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1137 GtkTreePath *tree_path,
1138 GtkTreeIter *tree_iter,
1139 ModestMsgViewWindow *window)
1141 ModestMsgViewWindowPrivate *priv = NULL;
1142 TnyHeader *header = NULL;
1144 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1145 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1147 g_assert (model == priv->header_model);
1149 /* Check if the newly inserted message is the same we are actually
1150 * showing. IF not, we should remain detached from the header model
1151 * and thus prev and next toolbar buttons should remain dimmed. */
1152 gtk_tree_model_get (model, tree_iter,
1153 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1156 if (TNY_IS_HEADER (header)) {
1159 uid = modest_tny_folder_get_header_unique_id (header);
1160 if (!g_str_equal(priv->msg_uid, uid)) {
1161 check_dimming_rules_after_change (window);
1163 g_object_unref (G_OBJECT(header));
1167 g_object_unref(G_OBJECT(header));
1170 if (priv->row_reference) {
1171 gtk_tree_row_reference_free (priv->row_reference);
1174 /* Setup row_reference for the actual msg. */
1175 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1176 if (priv->row_reference == NULL) {
1177 g_warning("No reference for msg header item.");
1181 /* Now set up next_row_reference. */
1182 if (priv->next_row_reference) {
1183 gtk_tree_row_reference_free (priv->next_row_reference);
1186 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1187 select_next_valid_row (priv->header_model,
1188 &(priv->next_row_reference), FALSE, priv->is_outbox);
1190 /* Connect the remaining callbacks to become able to detect
1191 * changes in header-view. */
1192 priv->row_changed_handler =
1193 g_signal_connect (priv->header_model, "row-changed",
1194 G_CALLBACK (modest_msg_view_window_on_row_changed),
1196 priv->row_deleted_handler =
1197 g_signal_connect (priv->header_model, "row-deleted",
1198 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1200 priv->rows_reordered_handler =
1201 g_signal_connect (priv->header_model, "rows-reordered",
1202 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1205 check_dimming_rules_after_change (window);
1209 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1213 ModestMsgViewWindow *window)
1215 ModestMsgViewWindowPrivate *priv = NULL;
1216 gboolean already_changed = FALSE;
1218 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1220 /* If the current row was reordered select the proper next
1221 valid row. The same if the next row reference changes */
1222 if (priv->row_reference &&
1223 gtk_tree_row_reference_valid (priv->row_reference)) {
1225 path = gtk_tree_row_reference_get_path (priv->row_reference);
1226 if (gtk_tree_path_compare (path, arg1) == 0) {
1227 if (priv->next_row_reference) {
1228 gtk_tree_row_reference_free (priv->next_row_reference);
1230 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1231 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1232 already_changed = TRUE;
1234 gtk_tree_path_free (path);
1236 if (!already_changed &&
1237 priv->next_row_reference &&
1238 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1240 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1241 if (gtk_tree_path_compare (path, arg1) == 0) {
1242 if (priv->next_row_reference) {
1243 gtk_tree_row_reference_free (priv->next_row_reference);
1245 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1246 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1248 gtk_tree_path_free (path);
1250 check_dimming_rules_after_change (window);
1253 /* The modest_msg_view_window_update_model_replaced implements update
1254 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1255 * actually belongs to the header-view is the same as the TnyFolder of
1256 * the message of msg-view or not. If they are different, there is
1257 * nothing to do. If they are the same, then the model has replaced and
1258 * the reference in msg-view shall be replaced from the old model to
1259 * the new model. In this case the view will be detached from it's
1260 * header folder. From this point the next/prev buttons are dimmed.
1263 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1264 GtkTreeModel *model,
1265 const gchar *tny_folder_id)
1267 ModestMsgViewWindowPrivate *priv = NULL;
1268 ModestMsgViewWindow *window = NULL;
1270 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1271 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1273 window = MODEST_MSG_VIEW_WINDOW(observer);
1274 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1276 /* If there is an other folder in the header-view then we do
1277 * not care about it's model (msg list). Else if the
1278 * header-view shows the folder the msg shown by us is in, we
1279 * shall replace our model reference and make some check. */
1280 if(model == NULL || tny_folder_id == NULL ||
1281 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1284 /* Model is changed(replaced), so we should forget the old
1285 * one. Because there might be other references and there
1286 * might be some change on the model even if we unreferenced
1287 * it, we need to disconnect our signals here. */
1288 if (priv->header_model) {
1289 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1290 priv->row_changed_handler))
1291 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1292 priv->row_changed_handler);
1293 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1294 priv->row_deleted_handler))
1295 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1296 priv->row_deleted_handler);
1297 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1298 priv->row_inserted_handler))
1299 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1300 priv->row_inserted_handler);
1301 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1302 priv->rows_reordered_handler))
1303 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1304 priv->rows_reordered_handler);
1307 if (priv->row_reference)
1308 gtk_tree_row_reference_free (priv->row_reference);
1309 if (priv->next_row_reference)
1310 gtk_tree_row_reference_free (priv->next_row_reference);
1311 g_object_unref(priv->header_model);
1314 priv->row_changed_handler = 0;
1315 priv->row_deleted_handler = 0;
1316 priv->row_inserted_handler = 0;
1317 priv->rows_reordered_handler = 0;
1318 priv->next_row_reference = NULL;
1319 priv->row_reference = NULL;
1320 priv->header_model = NULL;
1323 priv->header_model = g_object_ref (model);
1325 /* Also we must connect to the new model for row insertions.
1326 * Only for insertions now. We will need other ones only after
1327 * the msg is show by msg-view is added to the new model. */
1328 priv->row_inserted_handler =
1329 g_signal_connect (priv->header_model, "row-inserted",
1330 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1333 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1334 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1338 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1340 ModestMsgViewWindowPrivate *priv= NULL;
1342 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1343 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1345 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1349 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1351 ModestMsgViewWindowPrivate *priv= NULL;
1353 TnyHeader *header = NULL;
1354 GtkTreePath *path = NULL;
1357 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1358 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1360 /* If the message was not obtained from a treemodel,
1361 * for instance if it was opened directly by the search UI:
1363 if (priv->header_model == NULL ||
1364 priv->row_reference == NULL ||
1365 !gtk_tree_row_reference_valid (priv->row_reference)) {
1366 msg = modest_msg_view_window_get_message (self);
1368 header = tny_msg_get_header (msg);
1369 g_object_unref (msg);
1374 /* Get iter of the currently selected message in the header view: */
1375 path = gtk_tree_row_reference_get_path (priv->row_reference);
1376 g_return_val_if_fail (path != NULL, NULL);
1377 gtk_tree_model_get_iter (priv->header_model,
1381 /* Get current message header */
1382 gtk_tree_model_get (priv->header_model, &iter,
1383 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1386 gtk_tree_path_free (path);
1391 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1393 ModestMsgViewWindowPrivate *priv;
1395 g_return_val_if_fail (self, NULL);
1397 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1399 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1403 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1405 ModestMsgViewWindowPrivate *priv;
1407 g_return_val_if_fail (self, NULL);
1409 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1411 return (const gchar*) priv->msg_uid;
1415 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1418 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1419 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1420 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1424 is_active = gtk_toggle_action_get_active (toggle);
1427 gtk_widget_show (priv->find_toolbar);
1428 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1430 gtk_widget_hide (priv->find_toolbar);
1431 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1434 /* update the toggle buttons status */
1435 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1436 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1437 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1438 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1443 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1444 ModestMsgViewWindow *obj)
1446 GtkToggleAction *toggle;
1447 ModestWindowPrivate *parent_priv;
1448 ModestMsgViewWindowPrivate *priv;
1450 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1451 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1453 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1454 gtk_toggle_action_set_active (toggle, FALSE);
1455 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1459 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1460 ModestMsgViewWindow *obj)
1462 gchar *current_search;
1463 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1465 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1466 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1470 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1472 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1473 g_free (current_search);
1474 hildon_banner_show_information (NULL, NULL, _CS("ecdg_ib_find_rep_enter_text"));
1478 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1480 g_free (priv->last_search);
1481 priv->last_search = g_strdup (current_search);
1482 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1485 hildon_banner_show_information (NULL, NULL, _HL("ckct_ib_find_no_matches"));
1486 g_free (priv->last_search);
1487 priv->last_search = NULL;
1489 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1490 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1493 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1494 hildon_banner_show_information (NULL, NULL, _HL("ckct_ib_find_search_complete"));
1495 g_free (priv->last_search);
1496 priv->last_search = NULL;
1498 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1499 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1503 g_free (current_search);
1508 modest_msg_view_window_set_zoom (ModestWindow *window,
1511 ModestMsgViewWindowPrivate *priv;
1512 ModestWindowPrivate *parent_priv;
1513 GtkAction *action = NULL;
1514 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1516 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1518 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1519 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1520 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1522 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1523 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1525 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1529 modest_msg_view_window_get_zoom (ModestWindow *window)
1531 ModestMsgViewWindowPrivate *priv;
1533 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1535 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1536 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1540 modest_msg_view_window_zoom_plus (ModestWindow *window)
1542 ModestWindowPrivate *parent_priv;
1543 GtkRadioAction *zoom_radio_action;
1544 GSList *group, *node;
1546 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1547 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1548 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1550 group = gtk_radio_action_get_group (zoom_radio_action);
1552 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1553 hildon_banner_show_information (NULL, NULL, _CS("ckct_ib_max_zoom_level_reached"));
1557 for (node = group; node != NULL; node = g_slist_next (node)) {
1558 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1559 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1567 modest_msg_view_window_zoom_minus (ModestWindow *window)
1569 ModestWindowPrivate *parent_priv;
1570 GtkRadioAction *zoom_radio_action;
1571 GSList *group, *node;
1573 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1574 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1575 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1577 group = gtk_radio_action_get_group (zoom_radio_action);
1579 for (node = group; node != NULL; node = g_slist_next (node)) {
1580 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1581 if (node->next != NULL) {
1582 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1585 hildon_banner_show_information (NULL, NULL,
1586 _CS("ckct_ib_min_zoom_level_reached"));
1596 modest_msg_view_window_key_event (GtkWidget *window,
1602 focus = gtk_window_get_focus (GTK_WINDOW (window));
1604 /* for the find toolbar case */
1605 if (focus && GTK_IS_ENTRY (focus)) {
1606 if (event->keyval == GDK_BackSpace) {
1608 copy = gdk_event_copy ((GdkEvent *) event);
1609 gtk_widget_event (focus, copy);
1610 gdk_event_free (copy);
1615 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1616 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1617 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1618 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1619 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1620 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1621 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1622 /* gboolean return_value; */
1624 if (event->type == GDK_KEY_PRESS) {
1625 GtkScrollType scroll_type;
1627 switch (event->keyval) {
1630 scroll_type = GTK_SCROLL_STEP_UP; break;
1633 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1635 case GDK_KP_Page_Up:
1636 scroll_type = GTK_SCROLL_PAGE_UP; break;
1638 case GDK_KP_Page_Down:
1639 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1642 scroll_type = GTK_SCROLL_START; break;
1645 scroll_type = GTK_SCROLL_END; break;
1646 default: scroll_type = GTK_SCROLL_NONE;
1649 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1650 /* scroll_type, FALSE, &return_value); */
1661 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1664 ModestMsgViewWindowPrivate *priv;
1665 GtkTreeIter tmp_iter;
1666 gboolean is_last_selected;
1668 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1669 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1671 /*if no model (so no rows at all), then virtually we are the last*/
1672 if (!priv->header_model || !priv->row_reference)
1675 if (!gtk_tree_row_reference_valid (priv->row_reference))
1678 path = gtk_tree_row_reference_get_path (priv->row_reference);
1682 is_last_selected = TRUE;
1683 while (is_last_selected) {
1685 gtk_tree_path_next (path);
1686 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1688 gtk_tree_model_get (priv->header_model, &tmp_iter,
1689 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1692 if (msg_is_visible (header, priv->is_outbox))
1693 is_last_selected = FALSE;
1694 g_object_unref(G_OBJECT(header));
1697 gtk_tree_path_free (path);
1698 return is_last_selected;
1702 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1704 ModestMsgViewWindowPrivate *priv;
1706 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1707 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1709 return priv->header_model != NULL;
1713 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1715 ModestMsgViewWindowPrivate *priv;
1717 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1718 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1720 return priv->is_search_result;
1724 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1726 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1728 if (!check_outbox) {
1731 ModestTnySendQueueStatus status;
1732 status = modest_tny_all_send_queues_get_msg_status (header);
1733 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1734 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1739 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1742 ModestMsgViewWindowPrivate *priv;
1743 gboolean is_first_selected;
1744 GtkTreeIter tmp_iter;
1746 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1747 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1749 /*if no model (so no rows at all), then virtually we are the first*/
1750 if (!priv->header_model || !priv->row_reference)
1753 if (!gtk_tree_row_reference_valid (priv->row_reference))
1756 path = gtk_tree_row_reference_get_path (priv->row_reference);
1760 is_first_selected = TRUE;
1761 while (is_first_selected) {
1763 if(!gtk_tree_path_prev (path))
1765 /* Here the 'if' is needless for logic, but let make sure
1766 * iter is valid for gtk_tree_model_get. */
1767 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1769 gtk_tree_model_get (priv->header_model, &tmp_iter,
1770 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1773 if (msg_is_visible (header, priv->is_outbox))
1774 is_first_selected = FALSE;
1775 g_object_unref(G_OBJECT(header));
1778 gtk_tree_path_free (path);
1779 return is_first_selected;
1784 GtkTreeRowReference *row_reference;
1788 message_reader_performer (gboolean canceled,
1790 GtkWindow *parent_window,
1791 TnyAccount *account,
1794 ModestMailOperation *mail_op = NULL;
1795 MsgReaderInfo *info;
1797 info = (MsgReaderInfo *) user_data;
1798 if (canceled || err) {
1802 /* Register the header - it'll be unregistered in the callback */
1803 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1805 /* New mail operation */
1806 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1807 modest_ui_actions_disk_operations_error_handler,
1810 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1811 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1812 g_object_unref (mail_op);
1814 /* Update dimming rules */
1815 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1816 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1819 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1820 g_object_unref (info->header);
1821 g_slice_free (MsgReaderInfo, info);
1826 * Reads the message whose summary item is @header. It takes care of
1827 * several things, among others:
1829 * If the message was not previously downloaded then ask the user
1830 * before downloading. If there is no connection launch the connection
1831 * dialog. Update toolbar dimming rules.
1833 * Returns: TRUE if the mail operation was started, otherwise if the
1834 * user do not want to download the message, or if the user do not
1835 * want to connect, then the operation is not issued
1838 message_reader (ModestMsgViewWindow *window,
1839 ModestMsgViewWindowPrivate *priv,
1841 GtkTreeRowReference *row_reference)
1843 gboolean already_showing = FALSE;
1844 ModestWindow *msg_window = NULL;
1845 ModestWindowMgr *mgr;
1846 TnyAccount *account;
1848 MsgReaderInfo *info;
1850 g_return_val_if_fail (row_reference != NULL, FALSE);
1852 mgr = modest_runtime_get_window_mgr ();
1853 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1854 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1857 gtk_window_present (GTK_WINDOW (msg_window));
1858 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1862 /* Msg download completed */
1863 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1864 /* Ask the user if he wants to download the message if
1866 if (!tny_device_is_online (modest_runtime_get_device())) {
1867 GtkResponseType response;
1869 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1870 _("mcen_nc_get_msg"));
1871 if (response == GTK_RESPONSE_CANCEL)
1874 folder = tny_header_get_folder (header);
1875 info = g_slice_new (MsgReaderInfo);
1876 info->header = g_object_ref (header);
1877 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1879 /* Offer the connection dialog if necessary */
1880 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1882 TNY_FOLDER_STORE (folder),
1883 message_reader_performer,
1885 g_object_unref (folder);
1890 folder = tny_header_get_folder (header);
1891 account = tny_folder_get_account (folder);
1892 info = g_slice_new (MsgReaderInfo);
1893 info->header = g_object_ref (header);
1894 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1896 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1897 g_object_unref (account);
1898 g_object_unref (folder);
1904 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1906 ModestMsgViewWindowPrivate *priv;
1907 GtkTreePath *path= NULL;
1908 GtkTreeIter tmp_iter;
1910 gboolean retval = TRUE;
1911 GtkTreeRowReference *row_reference = NULL;
1913 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1914 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1916 if (!priv->row_reference)
1919 /* Update the next row reference if it's not valid. This could
1920 happen if for example the header which it was pointing to,
1921 was deleted. The best place to do it is in the row-deleted
1922 handler but the tinymail model do not work like the glib
1923 tree models and reports the deletion when the row is still
1925 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1926 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1927 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1928 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1931 if (priv->next_row_reference)
1932 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1936 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1938 gtk_tree_model_get_iter (priv->header_model,
1941 gtk_tree_path_free (path);
1943 gtk_tree_model_get (priv->header_model, &tmp_iter,
1944 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1947 /* Read the message & show it */
1948 if (!message_reader (window, priv, header, row_reference)) {
1951 gtk_tree_row_reference_free (row_reference);
1954 g_object_unref (header);
1960 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1962 ModestMsgViewWindowPrivate *priv = NULL;
1964 gboolean finished = FALSE;
1965 gboolean retval = FALSE;
1967 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1968 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1970 /* Return inmediatly if there is no header model */
1971 if (!priv->header_model || !priv->row_reference)
1974 path = gtk_tree_row_reference_get_path (priv->row_reference);
1975 while (!finished && gtk_tree_path_prev (path)) {
1979 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1980 gtk_tree_model_get (priv->header_model, &iter,
1981 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1985 if (msg_is_visible (header, priv->is_outbox)) {
1986 GtkTreeRowReference *row_reference;
1987 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1988 /* Read the message & show it */
1989 retval = message_reader (window, priv, header, row_reference);
1990 gtk_tree_row_reference_free (row_reference);
1994 g_object_unref (header);
1998 gtk_tree_path_free (path);
2003 view_msg_cb (ModestMailOperation *mail_op,
2010 ModestMsgViewWindow *self = NULL;
2011 ModestMsgViewWindowPrivate *priv = NULL;
2012 GtkTreeRowReference *row_reference = NULL;
2014 /* Unregister the header (it was registered before creating the mail operation) */
2015 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2017 row_reference = (GtkTreeRowReference *) user_data;
2019 gtk_tree_row_reference_free (row_reference);
2023 /* If there was any error */
2024 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2025 gtk_tree_row_reference_free (row_reference);
2029 /* Get the window */
2030 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2031 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2032 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2034 /* Update the row reference */
2035 if (priv->row_reference != NULL) {
2036 gtk_tree_row_reference_free (priv->row_reference);
2037 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2038 if (priv->next_row_reference != NULL) {
2039 gtk_tree_row_reference_free (priv->next_row_reference);
2041 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2042 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2045 /* Mark header as read */
2046 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2047 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2049 /* Set new message */
2050 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2051 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2052 modest_msg_view_window_update_priority (self);
2053 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2054 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2057 /* Set the new message uid of the window */
2058 if (priv->msg_uid) {
2059 g_free (priv->msg_uid);
2060 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2063 /* Notify the observers */
2064 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2065 0, priv->header_model, priv->row_reference);
2068 g_object_unref (self);
2069 gtk_tree_row_reference_free (row_reference);
2073 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2075 ModestMsgViewWindowPrivate *priv;
2077 TnyFolderType folder_type;
2079 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2081 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2083 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2087 folder = tny_msg_get_folder (msg);
2089 folder_type = modest_tny_folder_guess_folder_type (folder);
2090 g_object_unref (folder);
2092 g_object_unref (msg);
2100 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2102 ModestMsgViewWindowPrivate *priv;
2103 TnyHeader *header = NULL;
2104 TnyHeaderFlags flags = 0;
2106 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2108 if (priv->header_model && priv->row_reference) {
2110 GtkTreePath *path = NULL;
2112 path = gtk_tree_row_reference_get_path (priv->row_reference);
2113 g_return_if_fail (path != NULL);
2114 gtk_tree_model_get_iter (priv->header_model,
2116 gtk_tree_row_reference_get_path (priv->row_reference));
2118 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2120 gtk_tree_path_free (path);
2123 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2125 header = tny_msg_get_header (msg);
2126 g_object_unref (msg);
2131 flags = tny_header_get_flags (header);
2132 g_object_unref(G_OBJECT(header));
2135 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2140 toolbar_resize (ModestMsgViewWindow *self)
2142 ModestMsgViewWindowPrivate *priv = NULL;
2143 ModestWindowPrivate *parent_priv = NULL;
2145 gint static_button_size;
2146 ModestWindowMgr *mgr;
2148 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2149 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2150 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2152 mgr = modest_runtime_get_window_mgr ();
2153 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2155 if (parent_priv->toolbar) {
2156 /* left size buttons */
2157 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2158 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2159 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2160 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2161 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2162 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2163 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2164 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2165 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2166 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2167 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2168 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2169 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2170 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2171 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2172 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2174 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2175 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2176 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2177 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2178 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2179 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2180 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2181 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2187 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2189 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2190 ModestWindowPrivate *parent_priv;
2191 ModestWindowMgr *mgr;
2192 gboolean is_fullscreen;
2193 GtkAction *fs_toggle_action;
2196 mgr = modest_runtime_get_window_mgr ();
2197 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2199 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2201 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2202 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2203 if (is_fullscreen != active) {
2204 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2206 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2214 modest_msg_view_window_show_toolbar (ModestWindow *self,
2215 gboolean show_toolbar)
2217 ModestMsgViewWindowPrivate *priv = NULL;
2218 ModestWindowPrivate *parent_priv;
2219 GtkWidget *reply_button = NULL, *menu = NULL;
2220 GtkWidget *placeholder = NULL;
2222 const gchar *action_name;
2225 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2226 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2228 /* Set optimized view status */
2229 priv->optimized_view = !show_toolbar;
2231 if (!parent_priv->toolbar) {
2232 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2234 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2236 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2237 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2238 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2239 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2240 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2242 /* Add ProgressBar (Transfer toolbar) */
2243 priv->progress_bar = modest_progress_bar_new ();
2244 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2245 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2246 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2247 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2248 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2250 /* Connect cancel 'clicked' signal to abort progress mode */
2251 g_signal_connect(priv->cancel_toolitem, "clicked",
2252 G_CALLBACK(cancel_progressbar),
2255 /* Add it to the observers list */
2256 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2259 hildon_window_add_toolbar (HILDON_WINDOW (self),
2260 GTK_TOOLBAR (parent_priv->toolbar));
2262 /* Set reply button tap and hold menu */
2263 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2264 "/ToolBar/ToolbarMessageReply");
2265 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2266 "/ToolbarReplyCSM");
2267 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2271 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2272 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2273 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2275 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2276 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2277 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2279 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2282 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2283 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2286 /* Update also the actions (to update the toggles in the
2287 menus), we have to do it manually because some other window
2288 of the same time could have changed it (remember that the
2289 toolbar fullscreen mode is shared by all the windows of the
2291 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2292 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2294 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2296 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2297 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2302 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2304 ModestMsgViewWindow *window)
2306 if (!GTK_WIDGET_VISIBLE (window))
2309 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2313 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2315 ModestMsgViewWindowPrivate *priv;
2317 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2318 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2320 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2324 cancel_progressbar (GtkToolButton *toolbutton,
2325 ModestMsgViewWindow *self)
2328 ModestMsgViewWindowPrivate *priv;
2330 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2332 /* Get operation observers and cancel its current operation */
2333 tmp = priv->progress_widgets;
2335 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2336 tmp=g_slist_next(tmp);
2340 observers_empty (ModestMsgViewWindow *self)
2343 ModestMsgViewWindowPrivate *priv;
2344 gboolean is_empty = TRUE;
2345 guint pending_ops = 0;
2347 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2348 tmp = priv->progress_widgets;
2350 /* Check all observers */
2351 while (tmp && is_empty) {
2352 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2353 is_empty = pending_ops == 0;
2355 tmp = g_slist_next(tmp);
2362 on_account_removed (TnyAccountStore *account_store,
2363 TnyAccount *account,
2366 /* Do nothing if it's a transport account, because we only
2367 show the messages of a store account */
2368 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2369 const gchar *parent_acc = NULL;
2370 const gchar *our_acc = NULL;
2372 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2373 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2375 /* Close this window if I'm showing a message of the removed account */
2376 if (strcmp (parent_acc, our_acc) == 0)
2377 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2382 on_mail_operation_started (ModestMailOperation *mail_op,
2385 ModestMsgViewWindow *self;
2386 ModestMailOperationTypeOperation op_type;
2388 ModestMsgViewWindowPrivate *priv;
2389 GObject *source = NULL;
2391 self = MODEST_MSG_VIEW_WINDOW (user_data);
2392 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2393 op_type = modest_mail_operation_get_type_operation (mail_op);
2394 tmp = priv->progress_widgets;
2395 source = modest_mail_operation_get_source(mail_op);
2396 if (G_OBJECT (self) == source) {
2397 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2398 set_toolbar_transfer_mode(self);
2400 modest_progress_object_add_operation (
2401 MODEST_PROGRESS_OBJECT (tmp->data),
2403 tmp = g_slist_next (tmp);
2407 g_object_unref (source);
2411 on_mail_operation_finished (ModestMailOperation *mail_op,
2414 ModestMsgViewWindow *self;
2415 ModestMailOperationTypeOperation op_type;
2417 ModestMsgViewWindowPrivate *priv;
2419 self = MODEST_MSG_VIEW_WINDOW (user_data);
2420 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2421 op_type = modest_mail_operation_get_type_operation (mail_op);
2422 tmp = priv->progress_widgets;
2424 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2426 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2428 tmp = g_slist_next (tmp);
2431 /* If no more operations are being observed, NORMAL mode is enabled again */
2432 if (observers_empty (self)) {
2433 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2436 /* Update dimming rules. We have to do this right here
2437 and not in view_msg_cb because at that point the
2438 transfer mode is still enabled so the dimming rule
2439 won't let the user delete the message that has been
2440 readed for example */
2441 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2442 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2447 on_queue_changed (ModestMailOperationQueue *queue,
2448 ModestMailOperation *mail_op,
2449 ModestMailOperationQueueNotification type,
2450 ModestMsgViewWindow *self)
2452 ModestMsgViewWindowPrivate *priv;
2454 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2456 /* If this operations was created by another window, do nothing */
2457 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2460 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2461 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2463 "operation-started",
2464 G_CALLBACK (on_mail_operation_started),
2466 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2468 "operation-finished",
2469 G_CALLBACK (on_mail_operation_finished),
2471 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2472 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2474 "operation-started");
2475 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2477 "operation-finished");
2482 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2484 ModestMsgViewWindowPrivate *priv;
2485 TnyList *selected_attachments = NULL;
2487 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2488 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2490 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2492 return selected_attachments;
2498 guint banner_idle_id;
2499 } DecodeAsyncHelper;
2502 decode_async_banner_idle (gpointer user_data)
2504 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2506 helper->banner_idle_id = 0;
2507 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2508 g_object_ref (helper->banner);
2514 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2520 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2522 if (helper->banner_idle_id > 0) {
2523 g_source_remove (helper->banner_idle_id);
2524 helper->banner_idle_id = 0;
2526 if (helper->banner) {
2527 gtk_widget_destroy (helper->banner);
2529 if (cancelled || err) {
2530 modest_platform_information_banner (NULL, NULL,
2531 _("mail_ib_file_operation_failed"));
2535 /* make the file read-only */
2536 g_chmod(helper->filepath, 0444);
2538 /* Activate the file */
2539 modest_platform_activate_file (helper->filepath, modest_tny_mime_part_get_content_type (mime_part));
2543 g_free (helper->filepath);
2544 g_object_unref (helper->banner);
2545 g_slice_free (DecodeAsyncHelper, helper);
2549 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2550 TnyMimePart *mime_part)
2552 ModestMsgViewWindowPrivate *priv;
2553 const gchar *msg_uid;
2554 gchar *attachment_uid = NULL;
2555 gint attachment_index = 0;
2556 TnyList *attachments;
2557 TnyMimePart *window_msg;
2559 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2560 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2561 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2563 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2564 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2565 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2566 g_object_unref (attachments);
2568 if (msg_uid && attachment_index >= 0) {
2569 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2572 if (mime_part == NULL) {
2573 gboolean error = FALSE;
2574 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2575 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2577 } else if (tny_list_get_length (selected_attachments) > 1) {
2578 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2582 iter = tny_list_create_iterator (selected_attachments);
2583 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2584 g_object_unref (iter);
2586 g_object_unref (selected_attachments);
2591 g_object_ref (mime_part);
2594 if (tny_mime_part_is_purged (mime_part)) {
2595 g_object_unref (mime_part);
2599 /* we also check for mime_part == priv->msg, as this means it's a direct attachment
2600 * shown as attachment, so it should behave as a file */
2601 window_msg = TNY_MIME_PART (tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)));
2602 if (!modest_tny_mime_part_is_msg (mime_part)||
2603 mime_part == window_msg) {
2604 gchar *filepath = NULL;
2605 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2606 gboolean show_error_banner = FALSE;
2607 TnyFsStream *temp_stream = NULL;
2608 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2611 if (temp_stream != NULL) {
2612 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2613 helper->filepath = g_strdup (filepath);
2614 helper->banner = NULL;
2615 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2616 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2617 on_decode_to_stream_async_handler,
2620 g_object_unref (temp_stream);
2621 /* NOTE: files in the temporary area will be automatically
2622 * cleaned after some time if they are no longer in use */
2625 const gchar *content_type;
2626 /* the file may already exist but it isn't writable,
2627 * let's try to open it anyway */
2628 content_type = modest_tny_mime_part_get_content_type (mime_part);
2629 modest_platform_activate_file (filepath, content_type);
2631 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2632 show_error_banner = TRUE;
2637 if (show_error_banner)
2638 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2640 /* message attachment */
2641 TnyHeader *header = NULL;
2642 ModestWindowMgr *mgr;
2643 ModestWindow *msg_win = NULL;
2646 header = tny_msg_get_header (TNY_MSG (mime_part));
2647 mgr = modest_runtime_get_window_mgr ();
2648 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2651 if (msg_win) /* there is already a window for this uid; top it */
2652 gtk_window_present (GTK_WINDOW(msg_win));
2654 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2655 * thus, we don't do anything */
2656 g_warning ("window for is already being created");
2658 /* it's not found, so create a new window for it */
2659 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2660 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2662 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2663 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account,
2664 NULL, attachment_uid);
2665 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2666 modest_window_get_zoom (MODEST_WINDOW (window)));
2667 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2668 gtk_widget_show_all (GTK_WIDGET (msg_win));
2671 g_object_unref (window_msg);
2672 g_object_unref (mime_part);
2685 GnomeVFSResult result;
2688 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2689 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2690 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2691 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2694 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2698 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2699 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2700 g_free (pair->filename);
2701 g_object_unref (pair->part);
2702 g_slice_free (SaveMimePartPair, pair);
2704 g_list_free (info->pairs);
2707 gtk_widget_destroy (info->banner);
2708 g_slice_free (SaveMimePartInfo, info);
2713 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2715 if (info->pairs != NULL) {
2716 save_mime_part_to_file (info);
2718 /* This is a GDK lock because we are an idle callback and
2719 * hildon_banner_show_information is or does Gtk+ code */
2721 gdk_threads_enter (); /* CHECKED */
2722 save_mime_part_info_free (info, TRUE);
2723 if (info->result == GNOME_VFS_OK) {
2724 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2725 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2726 hildon_banner_show_information (NULL, NULL,
2727 _KR("cerm_device_memory_full"));
2729 hildon_banner_show_information (NULL, NULL,
2730 _("mail_ib_file_operation_failed"));
2732 gdk_threads_leave (); /* CHECKED */
2739 save_mime_part_to_file (SaveMimePartInfo *info)
2741 GnomeVFSHandle *handle;
2743 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2745 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2746 if (info->result == GNOME_VFS_OK) {
2747 GError *error = NULL;
2748 stream = tny_vfs_stream_new (handle);
2749 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2750 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2752 info->result = GNOME_VFS_ERROR_IO;
2754 g_object_unref (G_OBJECT (stream));
2755 g_object_unref (pair->part);
2756 g_slice_free (SaveMimePartPair, pair);
2757 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2759 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2760 save_mime_part_info_free (info, FALSE);
2763 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2768 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2770 gboolean is_ok = TRUE;
2771 gint replaced_files = 0;
2772 const GList *files = info->pairs;
2775 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2776 SaveMimePartPair *pair = iter->data;
2777 if (modest_utils_file_exists (pair->filename)) {
2781 if (replaced_files) {
2782 GtkWidget *confirm_overwrite_dialog;
2783 const gchar *message = (replaced_files == 1) ?
2784 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2785 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2786 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2789 gtk_widget_destroy (confirm_overwrite_dialog);
2793 save_mime_part_info_free (info, TRUE);
2795 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2796 _CS("sfil_ib_saving"));
2797 info->banner = banner;
2798 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2804 save_attachments_response (GtkDialog *dialog,
2808 TnyList *mime_parts;
2810 GList *files_to_save = NULL;
2812 mime_parts = TNY_LIST (user_data);
2814 if (arg1 != GTK_RESPONSE_OK)
2817 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2819 if (!modest_utils_folder_writable (chooser_uri)) {
2820 hildon_banner_show_information
2821 (NULL, NULL, _FM("sfil_ib_readonly_location"));
2825 iter = tny_list_create_iterator (mime_parts);
2826 while (!tny_iterator_is_done (iter)) {
2827 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2829 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2830 !tny_mime_part_is_purged (mime_part) &&
2831 (tny_mime_part_get_filename (mime_part) != NULL)) {
2832 SaveMimePartPair *pair;
2834 pair = g_slice_new0 (SaveMimePartPair);
2836 if (tny_list_get_length (mime_parts) > 1) {
2838 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2839 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2842 pair->filename = g_strdup (chooser_uri);
2844 pair->part = mime_part;
2845 files_to_save = g_list_prepend (files_to_save, pair);
2847 tny_iterator_next (iter);
2849 g_object_unref (iter);
2851 g_free (chooser_uri);
2853 if (files_to_save != NULL) {
2854 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2855 info->pairs = files_to_save;
2856 info->result = TRUE;
2857 save_mime_parts_to_file_with_checks (info);
2861 /* Free and close the dialog */
2862 g_object_unref (mime_parts);
2863 gtk_widget_destroy (GTK_WIDGET (dialog));
2867 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2869 ModestMsgViewWindowPrivate *priv;
2870 GtkWidget *save_dialog = NULL;
2871 gchar *folder = NULL;
2872 gchar *filename = NULL;
2873 gchar *save_multiple_str = NULL;
2876 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2877 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2879 if (mime_parts == NULL) {
2880 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2881 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2884 g_object_ref (mime_parts);
2887 window_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2888 /* prepare dialog */
2889 if (tny_list_get_length (mime_parts) == 1) {
2891 /* only one attachment selected */
2892 iter = tny_list_create_iterator (mime_parts);
2893 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2894 g_object_unref (iter);
2895 if (!modest_tny_mime_part_is_msg (mime_part) &&
2896 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2897 !tny_mime_part_is_purged (mime_part)) {
2898 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2900 /* TODO: show any error? */
2901 g_warning ("Tried to save a non-file attachment");
2902 g_object_unref (mime_parts);
2905 g_object_unref (mime_part);
2907 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2908 tny_list_get_length (mime_parts));
2910 g_object_unref (window_msg);
2912 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2913 GTK_FILE_CHOOSER_ACTION_SAVE);
2916 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2917 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2922 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2927 /* if multiple, set multiple string */
2928 if (save_multiple_str) {
2929 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2930 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2933 /* We must run this asynchronously, because the hildon dialog
2934 performs a gtk_dialog_run by itself which leads to gdk
2936 g_signal_connect (save_dialog, "response",
2937 G_CALLBACK (save_attachments_response), mime_parts);
2939 gtk_widget_show_all (save_dialog);
2943 show_remove_attachment_information (gpointer userdata)
2945 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2946 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2948 /* We're outside the main lock */
2949 gdk_threads_enter ();
2951 if (priv->remove_attachment_banner != NULL) {
2952 gtk_widget_destroy (priv->remove_attachment_banner);
2953 g_object_unref (priv->remove_attachment_banner);
2956 priv->remove_attachment_banner = g_object_ref (
2957 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2959 gdk_threads_leave ();
2965 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2967 ModestMsgViewWindowPrivate *priv;
2968 TnyList *mime_parts = NULL;
2969 gchar *confirmation_message;
2975 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2976 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2979 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2981 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2983 /* Remove already purged messages from mime parts list */
2984 iter = tny_list_create_iterator (mime_parts);
2985 while (!tny_iterator_is_done (iter)) {
2986 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2987 tny_iterator_next (iter);
2988 if (tny_mime_part_is_purged (part)) {
2989 tny_list_remove (mime_parts, (GObject *) part);
2991 g_object_unref (part);
2993 g_object_unref (iter);
2995 if (tny_list_get_length (mime_parts) == 0) {
2996 g_object_unref (mime_parts);
3000 n_attachments = tny_list_get_length (mime_parts);
3001 if (n_attachments == 1) {
3005 iter = tny_list_create_iterator (mime_parts);
3006 part = (TnyMimePart *) tny_iterator_get_current (iter);
3007 g_object_unref (iter);
3008 if (modest_tny_mime_part_is_msg (part)) {
3010 header = tny_msg_get_header (TNY_MSG (part));
3011 filename = tny_header_dup_subject (header);
3012 g_object_unref (header);
3013 if (filename == NULL)
3014 filename = g_strdup (_("mail_va_no_subject"));
3016 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3018 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3020 g_object_unref (part);
3022 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3023 "mcen_nc_purge_files_text",
3024 n_attachments), n_attachments);
3026 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3027 confirmation_message);
3028 g_free (confirmation_message);
3030 if (response != GTK_RESPONSE_OK) {
3031 g_object_unref (mime_parts);
3035 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3037 iter = tny_list_create_iterator (mime_parts);
3038 while (!tny_iterator_is_done (iter)) {
3041 part = (TnyMimePart *) tny_iterator_get_current (iter);
3042 tny_mime_part_set_purged (TNY_MIME_PART (part));
3043 g_object_unref (part);
3044 tny_iterator_next (iter);
3046 g_object_unref (iter);
3048 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3049 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3050 tny_msg_rewrite_cache (msg);
3051 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3052 g_object_unref (msg);
3054 g_object_unref (mime_parts);
3056 if (priv->purge_timeout > 0) {
3057 g_source_remove (priv->purge_timeout);
3058 priv->purge_timeout = 0;
3061 if (priv->remove_attachment_banner) {
3062 gtk_widget_destroy (priv->remove_attachment_banner);
3063 g_object_unref (priv->remove_attachment_banner);
3064 priv->remove_attachment_banner = NULL;
3072 update_window_title (ModestMsgViewWindow *window)
3074 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3076 TnyHeader *header = NULL;
3077 gchar *subject = NULL;
3079 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3082 header = tny_msg_get_header (msg);
3083 subject = tny_header_dup_subject (header);
3084 g_object_unref (header);
3085 g_object_unref (msg);
3088 if ((subject == NULL)||(subject[0] == '\0')) {
3090 subject = g_strdup (_("mail_va_no_subject"));
3093 gtk_window_set_title (GTK_WINDOW (window), subject);
3097 static void on_move_focus (GtkWidget *widget,
3098 GtkDirectionType direction,
3101 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3105 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3107 GnomeVFSResult result;
3108 GnomeVFSHandle *handle = NULL;
3109 GnomeVFSFileInfo *info = NULL;
3112 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3113 if (result != GNOME_VFS_OK) {
3118 info = gnome_vfs_file_info_new ();
3119 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3120 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3121 /* We put a "safe" default size for going to cache */
3122 *expected_size = (300*1024);
3124 *expected_size = info->size;
3126 gnome_vfs_file_info_unref (info);
3128 stream = tny_vfs_stream_new (handle);
3137 TnyStream *output_stream;
3138 GtkWidget *msg_view;
3142 on_fetch_image_idle_refresh_view (gpointer userdata)
3145 FetchImageData *fidata = (FetchImageData *) userdata;
3146 g_message ("REFRESH VIEW");
3147 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3148 g_message ("QUEUING DRAW");
3149 gtk_widget_queue_draw (fidata->msg_view);
3151 g_object_unref (fidata->msg_view);
3152 g_slice_free (FetchImageData, fidata);
3157 on_fetch_image_thread (gpointer userdata)
3159 FetchImageData *fidata = (FetchImageData *) userdata;
3160 TnyStreamCache *cache;
3161 TnyStream *cache_stream;
3163 cache = modest_runtime_get_images_cache ();
3164 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3165 g_free (fidata->cache_id);
3166 g_free (fidata->uri);
3168 if (cache_stream != NULL) {
3169 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3170 tny_stream_close (cache_stream);
3171 g_object_unref (cache_stream);
3174 tny_stream_close (fidata->output_stream);
3175 g_object_unref (fidata->output_stream);
3178 gdk_threads_enter ();
3179 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3180 gdk_threads_leave ();
3186 on_fetch_image (ModestMsgView *msgview,
3189 ModestMsgViewWindow *window)
3191 const gchar *current_account;
3192 ModestMsgViewWindowPrivate *priv;
3193 FetchImageData *fidata;
3195 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3197 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3199 fidata = g_slice_new0 (FetchImageData);
3200 fidata->msg_view = g_object_ref (msgview);
3201 fidata->uri = g_strdup (uri);
3202 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3203 fidata->output_stream = g_object_ref (stream);
3205 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3206 g_object_unref (fidata->output_stream);
3207 g_free (fidata->cache_id);
3208 g_free (fidata->uri);
3209 g_object_unref (fidata->msg_view);
3210 g_slice_free (FetchImageData, fidata);
3211 tny_stream_close (stream);
3219 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3221 modest_ui_actions_on_add_to_contacts (NULL, MODEST_WINDOW (self));