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 *msg_uid,
939 GtkTreeRowReference *row_reference)
941 ModestMsgViewWindow *window = NULL;
942 ModestMsgViewWindowPrivate *priv = NULL;
943 TnyFolder *header_folder = NULL;
944 ModestHeaderView *header_view = NULL;
945 ModestWindow *main_window = NULL;
946 ModestWindowMgr *mgr = NULL;
949 modest_tny_mime_part_to_string (TNY_MIME_PART (msg), 0);
952 mgr = modest_runtime_get_window_mgr ();
953 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
954 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
956 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
958 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
960 /* Remember the message list's TreeModel so we can detect changes
961 * and change the list selection when necessary: */
963 main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */
965 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget(
966 MODEST_MAIN_WINDOW(main_window),
967 MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
970 if (header_view != NULL){
971 header_folder = modest_header_view_get_folder(header_view);
972 /* This could happen if the header folder was
973 unseleted before opening this msg window (for
974 example if the user selects an account in the
975 folder view of the main window */
977 priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX);
978 priv->header_folder_id = tny_folder_get_id(header_folder);
979 g_assert(priv->header_folder_id != NULL);
980 g_object_unref(header_folder);
984 /* Setup row references and connect signals */
985 priv->header_model = g_object_ref (model);
988 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
989 priv->next_row_reference = gtk_tree_row_reference_copy (row_reference);
990 select_next_valid_row (model, &(priv->next_row_reference), TRUE, priv->is_outbox);
992 priv->row_reference = NULL;
993 priv->next_row_reference = NULL;
996 /* Connect signals */
997 priv->row_changed_handler =
998 g_signal_connect (GTK_TREE_MODEL(model), "row-changed",
999 G_CALLBACK(modest_msg_view_window_on_row_changed),
1001 priv->row_deleted_handler =
1002 g_signal_connect (GTK_TREE_MODEL(model), "row-deleted",
1003 G_CALLBACK(modest_msg_view_window_on_row_deleted),
1005 priv->row_inserted_handler =
1006 g_signal_connect (GTK_TREE_MODEL(model), "row-inserted",
1007 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1009 priv->rows_reordered_handler =
1010 g_signal_connect(GTK_TREE_MODEL(model), "rows-reordered",
1011 G_CALLBACK(modest_msg_view_window_on_row_reordered),
1014 if (header_view != NULL){
1015 modest_header_view_add_observer(header_view,
1016 MODEST_HEADER_VIEW_OBSERVER(window));
1019 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1020 update_window_title (MODEST_MSG_VIEW_WINDOW (window));
1021 gtk_widget_show_all (GTK_WIDGET (window));
1022 modest_msg_view_window_update_priority (window);
1024 /* Check dimming rules */
1025 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1026 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1027 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1029 return MODEST_WINDOW(window);
1033 modest_msg_view_window_new_for_search_result (TnyMsg *msg,
1034 const gchar *modest_account_name,
1035 const gchar *msg_uid)
1037 ModestMsgViewWindow *window = NULL;
1038 ModestMsgViewWindowPrivate *priv = NULL;
1039 ModestWindowMgr *mgr = NULL;
1041 mgr = modest_runtime_get_window_mgr ();
1042 window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr));
1043 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL);
1044 modest_msg_view_window_construct (window, modest_account_name, msg_uid);
1046 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1048 /* Remember that this is a search result,
1049 * so we can disable some UI appropriately: */
1050 priv->is_search_result = TRUE;
1052 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1054 update_window_title (window);
1055 gtk_widget_show_all (GTK_WIDGET (window));
1056 modest_msg_view_window_update_priority (window);
1058 /* Check dimming rules */
1059 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1060 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1061 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
1063 return MODEST_WINDOW(window);
1067 modest_msg_view_window_new_for_attachment (TnyMsg *msg,
1068 const gchar *modest_account_name,
1069 const gchar *msg_uid)
1071 GObject *obj = NULL;
1072 ModestMsgViewWindowPrivate *priv;
1073 ModestWindowMgr *mgr = NULL;
1075 g_return_val_if_fail (msg, NULL);
1076 mgr = modest_runtime_get_window_mgr ();
1077 obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr));
1078 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1079 modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj),
1080 modest_account_name, msg_uid);
1082 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
1083 update_window_title (MODEST_MSG_VIEW_WINDOW (obj));
1085 gtk_widget_show_all (GTK_WIDGET (obj));
1087 /* Check dimming rules */
1088 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj));
1089 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj));
1090 modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD);
1092 return MODEST_WINDOW(obj);
1096 modest_msg_view_window_on_row_changed (GtkTreeModel *header_model,
1099 ModestMsgViewWindow *window)
1101 check_dimming_rules_after_change (window);
1105 modest_msg_view_window_on_row_deleted(GtkTreeModel *header_model,
1107 ModestMsgViewWindow *window)
1109 check_dimming_rules_after_change (window);
1111 /* The window could have dissapeared */
1114 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1116 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window));
1117 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window));
1121 /* On insertions we check if the folder still has the message we are
1122 * showing or do not. If do not, we do nothing. Which means we are still
1123 * not attached to any header folder and thus next/prev buttons are
1124 * still dimmed. Once the message that is shown by msg-view is found, the
1125 * new model of header-view will be attached and the references will be set.
1126 * On each further insertions dimming rules will be checked. However
1127 * this requires extra CPU time at least works.
1128 * (An message might be deleted from TnyFolder and thus will not be
1129 * inserted into the model again for example if it is removed by the
1130 * imap server and the header view is refreshed.)
1133 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1134 GtkTreePath *tree_path,
1135 GtkTreeIter *tree_iter,
1136 ModestMsgViewWindow *window)
1138 ModestMsgViewWindowPrivate *priv = NULL;
1139 TnyHeader *header = NULL;
1141 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1142 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1144 g_assert (model == priv->header_model);
1146 /* Check if the newly inserted message is the same we are actually
1147 * showing. IF not, we should remain detached from the header model
1148 * and thus prev and next toolbar buttons should remain dimmed. */
1149 gtk_tree_model_get (model, tree_iter,
1150 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1153 if (TNY_IS_HEADER (header)) {
1156 uid = modest_tny_folder_get_header_unique_id (header);
1157 if (!g_str_equal(priv->msg_uid, uid)) {
1158 check_dimming_rules_after_change (window);
1160 g_object_unref (G_OBJECT(header));
1164 g_object_unref(G_OBJECT(header));
1167 if (priv->row_reference) {
1168 gtk_tree_row_reference_free (priv->row_reference);
1171 /* Setup row_reference for the actual msg. */
1172 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1173 if (priv->row_reference == NULL) {
1174 g_warning("No reference for msg header item.");
1178 /* Now set up next_row_reference. */
1179 if (priv->next_row_reference) {
1180 gtk_tree_row_reference_free (priv->next_row_reference);
1183 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1184 select_next_valid_row (priv->header_model,
1185 &(priv->next_row_reference), FALSE, priv->is_outbox);
1187 /* Connect the remaining callbacks to become able to detect
1188 * changes in header-view. */
1189 priv->row_changed_handler =
1190 g_signal_connect (priv->header_model, "row-changed",
1191 G_CALLBACK (modest_msg_view_window_on_row_changed),
1193 priv->row_deleted_handler =
1194 g_signal_connect (priv->header_model, "row-deleted",
1195 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1197 priv->rows_reordered_handler =
1198 g_signal_connect (priv->header_model, "rows-reordered",
1199 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1202 check_dimming_rules_after_change (window);
1206 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1210 ModestMsgViewWindow *window)
1212 ModestMsgViewWindowPrivate *priv = NULL;
1213 gboolean already_changed = FALSE;
1215 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1217 /* If the current row was reordered select the proper next
1218 valid row. The same if the next row reference changes */
1219 if (priv->row_reference &&
1220 gtk_tree_row_reference_valid (priv->row_reference)) {
1222 path = gtk_tree_row_reference_get_path (priv->row_reference);
1223 if (gtk_tree_path_compare (path, arg1) == 0) {
1224 if (priv->next_row_reference) {
1225 gtk_tree_row_reference_free (priv->next_row_reference);
1227 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1228 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1229 already_changed = TRUE;
1231 gtk_tree_path_free (path);
1233 if (!already_changed &&
1234 priv->next_row_reference &&
1235 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1237 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1238 if (gtk_tree_path_compare (path, arg1) == 0) {
1239 if (priv->next_row_reference) {
1240 gtk_tree_row_reference_free (priv->next_row_reference);
1242 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1243 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1245 gtk_tree_path_free (path);
1247 check_dimming_rules_after_change (window);
1250 /* The modest_msg_view_window_update_model_replaced implements update
1251 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1252 * actually belongs to the header-view is the same as the TnyFolder of
1253 * the message of msg-view or not. If they are different, there is
1254 * nothing to do. If they are the same, then the model has replaced and
1255 * the reference in msg-view shall be replaced from the old model to
1256 * the new model. In this case the view will be detached from it's
1257 * header folder. From this point the next/prev buttons are dimmed.
1260 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1261 GtkTreeModel *model,
1262 const gchar *tny_folder_id)
1264 ModestMsgViewWindowPrivate *priv = NULL;
1265 ModestMsgViewWindow *window = NULL;
1267 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1268 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1270 window = MODEST_MSG_VIEW_WINDOW(observer);
1271 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1273 /* If there is an other folder in the header-view then we do
1274 * not care about it's model (msg list). Else if the
1275 * header-view shows the folder the msg shown by us is in, we
1276 * shall replace our model reference and make some check. */
1277 if(model == NULL || tny_folder_id == NULL ||
1278 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1281 /* Model is changed(replaced), so we should forget the old
1282 * one. Because there might be other references and there
1283 * might be some change on the model even if we unreferenced
1284 * it, we need to disconnect our signals here. */
1285 if (priv->header_model) {
1286 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1287 priv->row_changed_handler))
1288 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1289 priv->row_changed_handler);
1290 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1291 priv->row_deleted_handler))
1292 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1293 priv->row_deleted_handler);
1294 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1295 priv->row_inserted_handler))
1296 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1297 priv->row_inserted_handler);
1298 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1299 priv->rows_reordered_handler))
1300 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1301 priv->rows_reordered_handler);
1304 if (priv->row_reference)
1305 gtk_tree_row_reference_free (priv->row_reference);
1306 if (priv->next_row_reference)
1307 gtk_tree_row_reference_free (priv->next_row_reference);
1308 g_object_unref(priv->header_model);
1311 priv->row_changed_handler = 0;
1312 priv->row_deleted_handler = 0;
1313 priv->row_inserted_handler = 0;
1314 priv->rows_reordered_handler = 0;
1315 priv->next_row_reference = NULL;
1316 priv->row_reference = NULL;
1317 priv->header_model = NULL;
1320 priv->header_model = g_object_ref (model);
1322 /* Also we must connect to the new model for row insertions.
1323 * Only for insertions now. We will need other ones only after
1324 * the msg is show by msg-view is added to the new model. */
1325 priv->row_inserted_handler =
1326 g_signal_connect (priv->header_model, "row-inserted",
1327 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1330 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1331 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1335 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1337 ModestMsgViewWindowPrivate *priv= NULL;
1339 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1340 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1342 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1346 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1348 ModestMsgViewWindowPrivate *priv= NULL;
1350 TnyHeader *header = NULL;
1351 GtkTreePath *path = NULL;
1354 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1355 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1357 /* If the message was not obtained from a treemodel,
1358 * for instance if it was opened directly by the search UI:
1360 if (priv->header_model == NULL ||
1361 priv->row_reference == NULL ||
1362 !gtk_tree_row_reference_valid (priv->row_reference)) {
1363 msg = modest_msg_view_window_get_message (self);
1365 header = tny_msg_get_header (msg);
1366 g_object_unref (msg);
1371 /* Get iter of the currently selected message in the header view: */
1372 path = gtk_tree_row_reference_get_path (priv->row_reference);
1373 g_return_val_if_fail (path != NULL, NULL);
1374 gtk_tree_model_get_iter (priv->header_model,
1378 /* Get current message header */
1379 gtk_tree_model_get (priv->header_model, &iter,
1380 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1383 gtk_tree_path_free (path);
1388 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1390 ModestMsgViewWindowPrivate *priv;
1392 g_return_val_if_fail (self, NULL);
1394 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1396 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1400 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1402 ModestMsgViewWindowPrivate *priv;
1404 g_return_val_if_fail (self, NULL);
1406 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1408 return (const gchar*) priv->msg_uid;
1412 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1415 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1416 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1417 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1421 is_active = gtk_toggle_action_get_active (toggle);
1424 gtk_widget_show (priv->find_toolbar);
1425 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1427 gtk_widget_hide (priv->find_toolbar);
1428 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1431 /* update the toggle buttons status */
1432 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1433 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1434 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1435 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1440 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1441 ModestMsgViewWindow *obj)
1443 GtkToggleAction *toggle;
1444 ModestWindowPrivate *parent_priv;
1445 ModestMsgViewWindowPrivate *priv;
1447 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1448 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1450 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1451 gtk_toggle_action_set_active (toggle, FALSE);
1452 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1456 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1457 ModestMsgViewWindow *obj)
1459 gchar *current_search;
1460 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1462 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1463 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1467 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1469 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1470 g_free (current_search);
1471 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1475 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1477 g_free (priv->last_search);
1478 priv->last_search = g_strdup (current_search);
1479 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1482 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1483 g_free (priv->last_search);
1484 priv->last_search = NULL;
1486 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1487 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1490 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1491 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1492 g_free (priv->last_search);
1493 priv->last_search = NULL;
1495 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1496 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1500 g_free (current_search);
1505 modest_msg_view_window_set_zoom (ModestWindow *window,
1508 ModestMsgViewWindowPrivate *priv;
1509 ModestWindowPrivate *parent_priv;
1510 GtkAction *action = NULL;
1511 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1513 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1515 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1516 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1517 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1519 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1520 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1522 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1526 modest_msg_view_window_get_zoom (ModestWindow *window)
1528 ModestMsgViewWindowPrivate *priv;
1530 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1532 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1533 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1537 modest_msg_view_window_zoom_plus (ModestWindow *window)
1539 ModestWindowPrivate *parent_priv;
1540 GtkRadioAction *zoom_radio_action;
1541 GSList *group, *node;
1543 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1544 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1545 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1547 group = gtk_radio_action_get_group (zoom_radio_action);
1549 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1550 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1554 for (node = group; node != NULL; node = g_slist_next (node)) {
1555 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1556 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1564 modest_msg_view_window_zoom_minus (ModestWindow *window)
1566 ModestWindowPrivate *parent_priv;
1567 GtkRadioAction *zoom_radio_action;
1568 GSList *group, *node;
1570 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1571 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1572 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1574 group = gtk_radio_action_get_group (zoom_radio_action);
1576 for (node = group; node != NULL; node = g_slist_next (node)) {
1577 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1578 if (node->next != NULL) {
1579 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1582 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1592 modest_msg_view_window_key_event (GtkWidget *window,
1598 focus = gtk_window_get_focus (GTK_WINDOW (window));
1600 /* for the find toolbar case */
1601 if (focus && GTK_IS_ENTRY (focus)) {
1602 if (event->keyval == GDK_BackSpace) {
1604 copy = gdk_event_copy ((GdkEvent *) event);
1605 gtk_widget_event (focus, copy);
1606 gdk_event_free (copy);
1611 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1612 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1613 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1614 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1615 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1616 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1617 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1618 /* gboolean return_value; */
1620 if (event->type == GDK_KEY_PRESS) {
1621 GtkScrollType scroll_type;
1623 switch (event->keyval) {
1626 scroll_type = GTK_SCROLL_STEP_UP; break;
1629 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1631 case GDK_KP_Page_Up:
1632 scroll_type = GTK_SCROLL_PAGE_UP; break;
1634 case GDK_KP_Page_Down:
1635 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1638 scroll_type = GTK_SCROLL_START; break;
1641 scroll_type = GTK_SCROLL_END; break;
1642 default: scroll_type = GTK_SCROLL_NONE;
1645 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1646 /* scroll_type, FALSE, &return_value); */
1657 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1660 ModestMsgViewWindowPrivate *priv;
1661 GtkTreeIter tmp_iter;
1662 gboolean is_last_selected;
1664 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1665 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1667 /*if no model (so no rows at all), then virtually we are the last*/
1668 if (!priv->header_model || !priv->row_reference)
1671 if (!gtk_tree_row_reference_valid (priv->row_reference))
1674 path = gtk_tree_row_reference_get_path (priv->row_reference);
1678 is_last_selected = TRUE;
1679 while (is_last_selected) {
1681 gtk_tree_path_next (path);
1682 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1684 gtk_tree_model_get (priv->header_model, &tmp_iter,
1685 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1688 if (msg_is_visible (header, priv->is_outbox))
1689 is_last_selected = FALSE;
1690 g_object_unref(G_OBJECT(header));
1693 gtk_tree_path_free (path);
1694 return is_last_selected;
1698 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1700 ModestMsgViewWindowPrivate *priv;
1702 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1703 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1705 return priv->header_model != NULL;
1709 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1711 ModestMsgViewWindowPrivate *priv;
1713 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1714 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1716 return priv->is_search_result;
1720 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1722 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1724 if (!check_outbox) {
1727 ModestTnySendQueueStatus status;
1728 status = modest_tny_all_send_queues_get_msg_status (header);
1729 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1730 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1735 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1738 ModestMsgViewWindowPrivate *priv;
1739 gboolean is_first_selected;
1740 GtkTreeIter tmp_iter;
1742 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1743 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1745 /*if no model (so no rows at all), then virtually we are the first*/
1746 if (!priv->header_model || !priv->row_reference)
1749 if (!gtk_tree_row_reference_valid (priv->row_reference))
1752 path = gtk_tree_row_reference_get_path (priv->row_reference);
1756 is_first_selected = TRUE;
1757 while (is_first_selected) {
1759 if(!gtk_tree_path_prev (path))
1761 /* Here the 'if' is needless for logic, but let make sure
1762 * iter is valid for gtk_tree_model_get. */
1763 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1765 gtk_tree_model_get (priv->header_model, &tmp_iter,
1766 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1769 if (msg_is_visible (header, priv->is_outbox))
1770 is_first_selected = FALSE;
1771 g_object_unref(G_OBJECT(header));
1774 gtk_tree_path_free (path);
1775 return is_first_selected;
1780 GtkTreeRowReference *row_reference;
1784 message_reader_performer (gboolean canceled,
1786 GtkWindow *parent_window,
1787 TnyAccount *account,
1790 ModestMailOperation *mail_op = NULL;
1791 MsgReaderInfo *info;
1793 info = (MsgReaderInfo *) user_data;
1794 if (canceled || err) {
1798 /* Register the header - it'll be unregistered in the callback */
1799 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1801 /* New mail operation */
1802 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1803 modest_ui_actions_disk_operations_error_handler,
1806 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1807 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1808 g_object_unref (mail_op);
1810 /* Update dimming rules */
1811 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1812 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1815 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1816 g_object_unref (info->header);
1817 g_slice_free (MsgReaderInfo, info);
1822 * Reads the message whose summary item is @header. It takes care of
1823 * several things, among others:
1825 * If the message was not previously downloaded then ask the user
1826 * before downloading. If there is no connection launch the connection
1827 * dialog. Update toolbar dimming rules.
1829 * Returns: TRUE if the mail operation was started, otherwise if the
1830 * user do not want to download the message, or if the user do not
1831 * want to connect, then the operation is not issued
1834 message_reader (ModestMsgViewWindow *window,
1835 ModestMsgViewWindowPrivate *priv,
1837 GtkTreeRowReference *row_reference)
1839 gboolean already_showing = FALSE;
1840 ModestWindow *msg_window = NULL;
1841 ModestWindowMgr *mgr;
1842 TnyAccount *account;
1844 MsgReaderInfo *info;
1846 g_return_val_if_fail (row_reference != NULL, FALSE);
1848 mgr = modest_runtime_get_window_mgr ();
1849 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1850 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1853 gtk_window_present (GTK_WINDOW (msg_window));
1854 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1858 /* Msg download completed */
1859 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1860 /* Ask the user if he wants to download the message if
1862 if (!tny_device_is_online (modest_runtime_get_device())) {
1863 GtkResponseType response;
1865 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1866 _("mcen_nc_get_msg"));
1867 if (response == GTK_RESPONSE_CANCEL)
1870 folder = tny_header_get_folder (header);
1871 info = g_slice_new (MsgReaderInfo);
1872 info->header = g_object_ref (header);
1873 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1875 /* Offer the connection dialog if necessary */
1876 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1878 TNY_FOLDER_STORE (folder),
1879 message_reader_performer,
1881 g_object_unref (folder);
1886 folder = tny_header_get_folder (header);
1887 account = tny_folder_get_account (folder);
1888 info = g_slice_new (MsgReaderInfo);
1889 info->header = g_object_ref (header);
1890 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1892 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1893 g_object_unref (account);
1894 g_object_unref (folder);
1900 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1902 ModestMsgViewWindowPrivate *priv;
1903 GtkTreePath *path= NULL;
1904 GtkTreeIter tmp_iter;
1906 gboolean retval = TRUE;
1907 GtkTreeRowReference *row_reference = NULL;
1909 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1910 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1912 if (!priv->row_reference)
1915 /* Update the next row reference if it's not valid. This could
1916 happen if for example the header which it was pointing to,
1917 was deleted. The best place to do it is in the row-deleted
1918 handler but the tinymail model do not work like the glib
1919 tree models and reports the deletion when the row is still
1921 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1922 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1923 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1924 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1927 if (priv->next_row_reference)
1928 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1932 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1934 gtk_tree_model_get_iter (priv->header_model,
1937 gtk_tree_path_free (path);
1939 gtk_tree_model_get (priv->header_model, &tmp_iter,
1940 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1943 /* Read the message & show it */
1944 if (!message_reader (window, priv, header, row_reference)) {
1947 gtk_tree_row_reference_free (row_reference);
1950 g_object_unref (header);
1956 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1958 ModestMsgViewWindowPrivate *priv = NULL;
1960 gboolean finished = FALSE;
1961 gboolean retval = FALSE;
1963 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1964 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1966 /* Return inmediatly if there is no header model */
1967 if (!priv->header_model || !priv->row_reference)
1970 path = gtk_tree_row_reference_get_path (priv->row_reference);
1971 while (!finished && gtk_tree_path_prev (path)) {
1975 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1976 gtk_tree_model_get (priv->header_model, &iter,
1977 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1981 if (msg_is_visible (header, priv->is_outbox)) {
1982 GtkTreeRowReference *row_reference;
1983 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
1984 /* Read the message & show it */
1985 retval = message_reader (window, priv, header, row_reference);
1986 gtk_tree_row_reference_free (row_reference);
1990 g_object_unref (header);
1994 gtk_tree_path_free (path);
1999 view_msg_cb (ModestMailOperation *mail_op,
2006 ModestMsgViewWindow *self = NULL;
2007 ModestMsgViewWindowPrivate *priv = NULL;
2008 GtkTreeRowReference *row_reference = NULL;
2010 /* Unregister the header (it was registered before creating the mail operation) */
2011 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2013 row_reference = (GtkTreeRowReference *) user_data;
2015 gtk_tree_row_reference_free (row_reference);
2019 /* If there was any error */
2020 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2021 gtk_tree_row_reference_free (row_reference);
2025 /* Get the window */
2026 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2027 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2028 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2030 /* Update the row reference */
2031 if (priv->row_reference != NULL) {
2032 gtk_tree_row_reference_free (priv->row_reference);
2033 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2034 if (priv->next_row_reference != NULL) {
2035 gtk_tree_row_reference_free (priv->next_row_reference);
2037 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2038 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2041 /* Mark header as read */
2042 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2043 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2045 /* Set new message */
2046 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2047 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2048 modest_msg_view_window_update_priority (self);
2049 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2050 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2053 /* Set the new message uid of the window */
2054 if (priv->msg_uid) {
2055 g_free (priv->msg_uid);
2056 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2059 /* Notify the observers */
2060 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2061 0, priv->header_model, priv->row_reference);
2064 g_object_unref (self);
2065 gtk_tree_row_reference_free (row_reference);
2069 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2071 ModestMsgViewWindowPrivate *priv;
2073 TnyFolderType folder_type;
2075 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2077 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2079 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2083 folder = tny_msg_get_folder (msg);
2085 folder_type = modest_tny_folder_guess_folder_type (folder);
2086 g_object_unref (folder);
2088 g_object_unref (msg);
2096 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2098 ModestMsgViewWindowPrivate *priv;
2099 TnyHeader *header = NULL;
2100 TnyHeaderFlags flags = 0;
2102 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2104 if (priv->header_model && priv->row_reference) {
2106 GtkTreePath *path = NULL;
2108 path = gtk_tree_row_reference_get_path (priv->row_reference);
2109 g_return_if_fail (path != NULL);
2110 gtk_tree_model_get_iter (priv->header_model,
2112 gtk_tree_row_reference_get_path (priv->row_reference));
2114 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2116 gtk_tree_path_free (path);
2119 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2121 header = tny_msg_get_header (msg);
2122 g_object_unref (msg);
2127 flags = tny_header_get_flags (header);
2128 g_object_unref(G_OBJECT(header));
2131 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2136 toolbar_resize (ModestMsgViewWindow *self)
2138 ModestMsgViewWindowPrivate *priv = NULL;
2139 ModestWindowPrivate *parent_priv = NULL;
2141 gint static_button_size;
2142 ModestWindowMgr *mgr;
2144 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2145 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2146 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2148 mgr = modest_runtime_get_window_mgr ();
2149 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2151 if (parent_priv->toolbar) {
2152 /* left size buttons */
2153 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2154 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2155 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2156 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2157 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
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/ToolbarDeleteMessage");
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/FindInMessage");
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);
2170 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2171 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2172 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2173 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2174 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2175 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2176 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2177 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2183 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2185 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2186 ModestWindowPrivate *parent_priv;
2187 ModestWindowMgr *mgr;
2188 gboolean is_fullscreen;
2189 GtkAction *fs_toggle_action;
2192 mgr = modest_runtime_get_window_mgr ();
2193 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2195 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2197 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2198 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2199 if (is_fullscreen != active) {
2200 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2202 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2210 modest_msg_view_window_show_toolbar (ModestWindow *self,
2211 gboolean show_toolbar)
2213 ModestMsgViewWindowPrivate *priv = NULL;
2214 ModestWindowPrivate *parent_priv;
2215 GtkWidget *reply_button = NULL, *menu = NULL;
2216 GtkWidget *placeholder = NULL;
2218 const gchar *action_name;
2221 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2222 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2224 /* Set optimized view status */
2225 priv->optimized_view = !show_toolbar;
2227 if (!parent_priv->toolbar) {
2228 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2230 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2232 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2233 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2234 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2235 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2236 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2238 /* Add ProgressBar (Transfer toolbar) */
2239 priv->progress_bar = modest_progress_bar_new ();
2240 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2241 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2242 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2243 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2244 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2246 /* Connect cancel 'clicked' signal to abort progress mode */
2247 g_signal_connect(priv->cancel_toolitem, "clicked",
2248 G_CALLBACK(cancel_progressbar),
2251 /* Add it to the observers list */
2252 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2255 hildon_window_add_toolbar (HILDON_WINDOW (self),
2256 GTK_TOOLBAR (parent_priv->toolbar));
2258 /* Set reply button tap and hold menu */
2259 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2260 "/ToolBar/ToolbarMessageReply");
2261 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2262 "/ToolbarReplyCSM");
2263 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2267 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2268 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2269 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2271 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2272 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2273 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2275 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2278 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2279 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2282 /* Update also the actions (to update the toggles in the
2283 menus), we have to do it manually because some other window
2284 of the same time could have changed it (remember that the
2285 toolbar fullscreen mode is shared by all the windows of the
2287 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2288 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2290 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2292 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2293 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2298 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2300 ModestMsgViewWindow *window)
2302 if (!GTK_WIDGET_VISIBLE (window))
2305 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2309 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2311 ModestMsgViewWindowPrivate *priv;
2313 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2314 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2316 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2320 cancel_progressbar (GtkToolButton *toolbutton,
2321 ModestMsgViewWindow *self)
2324 ModestMsgViewWindowPrivate *priv;
2326 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2328 /* Get operation observers and cancel its current operation */
2329 tmp = priv->progress_widgets;
2331 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2332 tmp=g_slist_next(tmp);
2336 observers_empty (ModestMsgViewWindow *self)
2339 ModestMsgViewWindowPrivate *priv;
2340 gboolean is_empty = TRUE;
2341 guint pending_ops = 0;
2343 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2344 tmp = priv->progress_widgets;
2346 /* Check all observers */
2347 while (tmp && is_empty) {
2348 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2349 is_empty = pending_ops == 0;
2351 tmp = g_slist_next(tmp);
2358 on_account_removed (TnyAccountStore *account_store,
2359 TnyAccount *account,
2362 /* Do nothing if it's a transport account, because we only
2363 show the messages of a store account */
2364 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2365 const gchar *parent_acc = NULL;
2366 const gchar *our_acc = NULL;
2368 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2369 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2371 /* Close this window if I'm showing a message of the removed account */
2372 if (strcmp (parent_acc, our_acc) == 0)
2373 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2378 on_mail_operation_started (ModestMailOperation *mail_op,
2381 ModestMsgViewWindow *self;
2382 ModestMailOperationTypeOperation op_type;
2384 ModestMsgViewWindowPrivate *priv;
2385 GObject *source = NULL;
2387 self = MODEST_MSG_VIEW_WINDOW (user_data);
2388 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2389 op_type = modest_mail_operation_get_type_operation (mail_op);
2390 tmp = priv->progress_widgets;
2391 source = modest_mail_operation_get_source(mail_op);
2392 if (G_OBJECT (self) == source) {
2393 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2394 set_toolbar_transfer_mode(self);
2396 modest_progress_object_add_operation (
2397 MODEST_PROGRESS_OBJECT (tmp->data),
2399 tmp = g_slist_next (tmp);
2403 g_object_unref (source);
2407 on_mail_operation_finished (ModestMailOperation *mail_op,
2410 ModestMsgViewWindow *self;
2411 ModestMailOperationTypeOperation op_type;
2413 ModestMsgViewWindowPrivate *priv;
2415 self = MODEST_MSG_VIEW_WINDOW (user_data);
2416 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2417 op_type = modest_mail_operation_get_type_operation (mail_op);
2418 tmp = priv->progress_widgets;
2420 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE ) {
2422 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2424 tmp = g_slist_next (tmp);
2427 /* If no more operations are being observed, NORMAL mode is enabled again */
2428 if (observers_empty (self)) {
2429 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2432 /* Update dimming rules. We have to do this right here
2433 and not in view_msg_cb because at that point the
2434 transfer mode is still enabled so the dimming rule
2435 won't let the user delete the message that has been
2436 readed for example */
2437 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2438 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2443 on_queue_changed (ModestMailOperationQueue *queue,
2444 ModestMailOperation *mail_op,
2445 ModestMailOperationQueueNotification type,
2446 ModestMsgViewWindow *self)
2448 ModestMsgViewWindowPrivate *priv;
2450 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2452 /* If this operations was created by another window, do nothing */
2453 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2456 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2457 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2459 "operation-started",
2460 G_CALLBACK (on_mail_operation_started),
2462 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2464 "operation-finished",
2465 G_CALLBACK (on_mail_operation_finished),
2467 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2468 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2470 "operation-started");
2471 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2473 "operation-finished");
2478 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2480 ModestMsgViewWindowPrivate *priv;
2481 TnyList *selected_attachments = NULL;
2483 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2484 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2486 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2488 return selected_attachments;
2494 guint banner_idle_id;
2495 } DecodeAsyncHelper;
2498 decode_async_banner_idle (gpointer user_data)
2500 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2502 helper->banner_idle_id = 0;
2503 helper->banner = hildon_banner_show_animation (NULL, NULL, _("mail_me_opening"));
2504 g_object_ref (helper->banner);
2510 on_decode_to_stream_async_handler (TnyMimePart *mime_part,
2516 DecodeAsyncHelper *helper = (DecodeAsyncHelper *) user_data;
2518 if (helper->banner_idle_id > 0) {
2519 g_source_remove (helper->banner_idle_id);
2520 helper->banner_idle_id = 0;
2522 if (helper->banner) {
2523 gtk_widget_destroy (helper->banner);
2525 if (cancelled || err) {
2526 modest_platform_information_banner (NULL, NULL,
2527 _("mail_ib_file_operation_failed"));
2531 /* make the file read-only */
2532 g_chmod(helper->filepath, 0444);
2534 /* Activate the file */
2535 modest_platform_activate_file (helper->filepath, modest_tny_mime_part_get_content_type (mime_part));
2539 g_free (helper->filepath);
2540 g_object_unref (helper->banner);
2541 g_slice_free (DecodeAsyncHelper, helper);
2545 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window,
2546 TnyMimePart *mime_part)
2548 ModestMsgViewWindowPrivate *priv;
2549 const gchar *msg_uid;
2550 gchar *attachment_uid = NULL;
2551 gint attachment_index = 0;
2552 TnyList *attachments;
2553 TnyMimePart *window_msg;
2555 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2556 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2557 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2559 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2560 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2561 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2562 g_object_unref (attachments);
2564 if (msg_uid && attachment_index >= 0) {
2565 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2568 if (mime_part == NULL) {
2569 gboolean error = FALSE;
2570 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2571 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2573 } else if (tny_list_get_length (selected_attachments) > 1) {
2574 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2578 iter = tny_list_create_iterator (selected_attachments);
2579 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2580 g_object_unref (iter);
2582 g_object_unref (selected_attachments);
2587 g_object_ref (mime_part);
2590 if (tny_mime_part_is_purged (mime_part)) {
2591 g_object_unref (mime_part);
2595 /* we also check for mime_part == priv->msg, as this means it's a direct attachment
2596 * shown as attachment, so it should behave as a file */
2597 window_msg = TNY_MIME_PART (tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)));
2598 if (!modest_tny_mime_part_is_msg (mime_part)||
2599 mime_part == window_msg) {
2600 gchar *filepath = NULL;
2601 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2602 gboolean show_error_banner = FALSE;
2603 TnyFsStream *temp_stream = NULL;
2604 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2607 if (temp_stream != NULL) {
2608 DecodeAsyncHelper *helper = g_slice_new (DecodeAsyncHelper);
2609 helper->filepath = g_strdup (filepath);
2610 helper->banner = NULL;
2611 helper->banner_idle_id = g_timeout_add (1000, decode_async_banner_idle, helper);
2612 tny_mime_part_decode_to_stream_async (mime_part, TNY_STREAM (temp_stream),
2613 on_decode_to_stream_async_handler,
2616 g_object_unref (temp_stream);
2617 /* NOTE: files in the temporary area will be automatically
2618 * cleaned after some time if they are no longer in use */
2621 const gchar *content_type;
2622 /* the file may already exist but it isn't writable,
2623 * let's try to open it anyway */
2624 content_type = modest_tny_mime_part_get_content_type (mime_part);
2625 modest_platform_activate_file (filepath, content_type);
2627 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2628 show_error_banner = TRUE;
2633 if (show_error_banner)
2634 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2636 /* message attachment */
2637 TnyHeader *header = NULL;
2638 ModestWindowMgr *mgr;
2639 ModestWindow *msg_win = NULL;
2642 header = tny_msg_get_header (TNY_MSG (mime_part));
2643 mgr = modest_runtime_get_window_mgr ();
2644 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2647 if (msg_win) /* there is already a window for this uid; top it */
2648 gtk_window_present (GTK_WINDOW(msg_win));
2650 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2651 * thus, we don't do anything */
2652 g_warning ("window for is already being created");
2654 /* it's not found, so create a new window for it */
2655 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2656 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2658 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2659 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2660 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2661 modest_window_get_zoom (MODEST_WINDOW (window)));
2662 modest_window_mgr_register_window (mgr, msg_win, MODEST_WINDOW (window));
2663 gtk_widget_show_all (GTK_WIDGET (msg_win));
2666 g_object_unref (window_msg);
2667 g_object_unref (mime_part);
2680 GnomeVFSResult result;
2683 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2684 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2685 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2686 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2689 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2693 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2694 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2695 g_free (pair->filename);
2696 g_object_unref (pair->part);
2697 g_slice_free (SaveMimePartPair, pair);
2699 g_list_free (info->pairs);
2702 gtk_widget_destroy (info->banner);
2703 g_slice_free (SaveMimePartInfo, info);
2708 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2710 if (info->pairs != NULL) {
2711 save_mime_part_to_file (info);
2713 /* This is a GDK lock because we are an idle callback and
2714 * hildon_banner_show_information is or does Gtk+ code */
2716 gdk_threads_enter (); /* CHECKED */
2717 save_mime_part_info_free (info, TRUE);
2718 if (info->result == GNOME_VFS_OK) {
2719 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2720 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2721 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2722 "cerm_device_memory_full"));
2724 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2726 gdk_threads_leave (); /* CHECKED */
2733 save_mime_part_to_file (SaveMimePartInfo *info)
2735 GnomeVFSHandle *handle;
2737 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2739 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2740 if (info->result == GNOME_VFS_OK) {
2741 GError *error = NULL;
2742 stream = tny_vfs_stream_new (handle);
2743 if (tny_mime_part_decode_to_stream (pair->part, stream, &error) < 0) {
2744 g_warning ("modest: could not save attachment %s: %d (%s)\n", pair->filename, error?error->code:-1, error?error->message:"Unknown error");
2746 info->result = GNOME_VFS_ERROR_IO;
2748 g_object_unref (G_OBJECT (stream));
2749 g_object_unref (pair->part);
2750 g_slice_free (SaveMimePartPair, pair);
2751 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2753 g_warning ("modest: could not create save attachment %s: %s\n", pair->filename, gnome_vfs_result_to_string (info->result));
2754 save_mime_part_info_free (info, FALSE);
2757 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2762 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2764 gboolean is_ok = TRUE;
2765 gint replaced_files = 0;
2766 const GList *files = info->pairs;
2769 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2770 SaveMimePartPair *pair = iter->data;
2771 if (modest_utils_file_exists (pair->filename)) {
2775 if (replaced_files) {
2776 GtkWidget *confirm_overwrite_dialog;
2777 const gchar *message = (replaced_files == 1) ?
2778 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2779 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2780 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2783 gtk_widget_destroy (confirm_overwrite_dialog);
2787 save_mime_part_info_free (info, TRUE);
2789 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2790 _CS("sfil_ib_saving"));
2791 info->banner = banner;
2792 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2798 save_attachments_response (GtkDialog *dialog,
2802 TnyList *mime_parts;
2804 GList *files_to_save = NULL;
2806 mime_parts = TNY_LIST (user_data);
2808 if (arg1 != GTK_RESPONSE_OK)
2811 chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2813 if (!modest_utils_folder_writable (chooser_uri)) {
2814 hildon_banner_show_information
2815 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2819 iter = tny_list_create_iterator (mime_parts);
2820 while (!tny_iterator_is_done (iter)) {
2821 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2823 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2824 !tny_mime_part_is_purged (mime_part) &&
2825 (tny_mime_part_get_filename (mime_part) != NULL)) {
2826 SaveMimePartPair *pair;
2828 pair = g_slice_new0 (SaveMimePartPair);
2830 if (tny_list_get_length (mime_parts) > 1) {
2832 gnome_vfs_escape_slashes (tny_mime_part_get_filename (mime_part));
2833 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2836 pair->filename = g_strdup (chooser_uri);
2838 pair->part = mime_part;
2839 files_to_save = g_list_prepend (files_to_save, pair);
2841 tny_iterator_next (iter);
2843 g_object_unref (iter);
2845 g_free (chooser_uri);
2847 if (files_to_save != NULL) {
2848 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2849 info->pairs = files_to_save;
2850 info->result = TRUE;
2851 save_mime_parts_to_file_with_checks (info);
2855 /* Free and close the dialog */
2856 g_object_unref (mime_parts);
2857 gtk_widget_destroy (GTK_WIDGET (dialog));
2861 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2863 ModestMsgViewWindowPrivate *priv;
2864 GtkWidget *save_dialog = NULL;
2865 gchar *folder = NULL;
2866 gchar *filename = NULL;
2867 gchar *save_multiple_str = NULL;
2870 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2871 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2873 if (mime_parts == NULL) {
2874 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2875 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2878 g_object_ref (mime_parts);
2881 window_msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2882 /* prepare dialog */
2883 if (tny_list_get_length (mime_parts) == 1) {
2885 /* only one attachment selected */
2886 iter = tny_list_create_iterator (mime_parts);
2887 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2888 g_object_unref (iter);
2889 if (!modest_tny_mime_part_is_msg (mime_part) &&
2890 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2891 !tny_mime_part_is_purged (mime_part)) {
2892 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2894 /* TODO: show any error? */
2895 g_warning ("Tried to save a non-file attachment");
2896 g_object_unref (mime_parts);
2899 g_object_unref (mime_part);
2901 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2902 tny_list_get_length (mime_parts));
2904 g_object_unref (window_msg);
2906 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2907 GTK_FILE_CHOOSER_ACTION_SAVE);
2910 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2911 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2916 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2921 /* if multiple, set multiple string */
2922 if (save_multiple_str) {
2923 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2924 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2927 /* We must run this asynchronously, because the hildon dialog
2928 performs a gtk_dialog_run by itself which leads to gdk
2930 g_signal_connect (save_dialog, "response",
2931 G_CALLBACK (save_attachments_response), mime_parts);
2933 gtk_widget_show_all (save_dialog);
2937 show_remove_attachment_information (gpointer userdata)
2939 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2940 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2942 /* We're outside the main lock */
2943 gdk_threads_enter ();
2945 if (priv->remove_attachment_banner != NULL) {
2946 gtk_widget_destroy (priv->remove_attachment_banner);
2947 g_object_unref (priv->remove_attachment_banner);
2950 priv->remove_attachment_banner = g_object_ref (
2951 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2953 gdk_threads_leave ();
2959 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2961 ModestMsgViewWindowPrivate *priv;
2962 TnyList *mime_parts = NULL;
2963 gchar *confirmation_message;
2969 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2970 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2973 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2975 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2977 /* Remove already purged messages from mime parts list */
2978 iter = tny_list_create_iterator (mime_parts);
2979 while (!tny_iterator_is_done (iter)) {
2980 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2981 tny_iterator_next (iter);
2982 if (tny_mime_part_is_purged (part)) {
2983 tny_list_remove (mime_parts, (GObject *) part);
2985 g_object_unref (part);
2987 g_object_unref (iter);
2989 if (tny_list_get_length (mime_parts) == 0) {
2990 g_object_unref (mime_parts);
2994 n_attachments = tny_list_get_length (mime_parts);
2995 if (n_attachments == 1) {
2999 iter = tny_list_create_iterator (mime_parts);
3000 part = (TnyMimePart *) tny_iterator_get_current (iter);
3001 g_object_unref (iter);
3002 if (modest_tny_mime_part_is_msg (part)) {
3004 header = tny_msg_get_header (TNY_MSG (part));
3005 filename = tny_header_dup_subject (header);
3006 g_object_unref (header);
3007 if (filename == NULL)
3008 filename = g_strdup (_("mail_va_no_subject"));
3010 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
3012 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
3014 g_object_unref (part);
3016 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
3017 "mcen_nc_purge_files_text",
3018 n_attachments), n_attachments);
3020 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
3021 confirmation_message);
3022 g_free (confirmation_message);
3024 if (response != GTK_RESPONSE_OK) {
3025 g_object_unref (mime_parts);
3029 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
3031 iter = tny_list_create_iterator (mime_parts);
3032 while (!tny_iterator_is_done (iter)) {
3035 part = (TnyMimePart *) tny_iterator_get_current (iter);
3036 tny_mime_part_set_purged (TNY_MIME_PART (part));
3037 g_object_unref (part);
3038 tny_iterator_next (iter);
3040 g_object_unref (iter);
3042 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3043 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
3044 tny_msg_rewrite_cache (msg);
3045 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
3046 g_object_unref (msg);
3048 g_object_unref (mime_parts);
3050 if (priv->purge_timeout > 0) {
3051 g_source_remove (priv->purge_timeout);
3052 priv->purge_timeout = 0;
3055 if (priv->remove_attachment_banner) {
3056 gtk_widget_destroy (priv->remove_attachment_banner);
3057 g_object_unref (priv->remove_attachment_banner);
3058 priv->remove_attachment_banner = NULL;
3066 update_window_title (ModestMsgViewWindow *window)
3068 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3070 TnyHeader *header = NULL;
3071 gchar *subject = NULL;
3073 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3076 header = tny_msg_get_header (msg);
3077 subject = tny_header_dup_subject (header);
3078 g_object_unref (header);
3079 g_object_unref (msg);
3082 if ((subject == NULL)||(subject[0] == '\0')) {
3084 subject = g_strdup (_("mail_va_no_subject"));
3087 gtk_window_set_title (GTK_WINDOW (window), subject);
3091 static void on_move_focus (GtkWidget *widget,
3092 GtkDirectionType direction,
3095 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3099 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3101 GnomeVFSResult result;
3102 GnomeVFSHandle *handle = NULL;
3103 GnomeVFSFileInfo *info = NULL;
3106 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3107 if (result != GNOME_VFS_OK) {
3112 info = gnome_vfs_file_info_new ();
3113 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3114 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3115 /* We put a "safe" default size for going to cache */
3116 *expected_size = (300*1024);
3118 *expected_size = info->size;
3120 gnome_vfs_file_info_unref (info);
3122 stream = tny_vfs_stream_new (handle);
3131 TnyStream *output_stream;
3132 GtkWidget *msg_view;
3136 on_fetch_image_idle_refresh_view (gpointer userdata)
3139 FetchImageData *fidata = (FetchImageData *) userdata;
3140 g_message ("REFRESH VIEW");
3141 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3142 g_message ("QUEUING DRAW");
3143 gtk_widget_queue_draw (fidata->msg_view);
3145 g_object_unref (fidata->msg_view);
3146 g_slice_free (FetchImageData, fidata);
3151 on_fetch_image_thread (gpointer userdata)
3153 FetchImageData *fidata = (FetchImageData *) userdata;
3154 TnyStreamCache *cache;
3155 TnyStream *cache_stream;
3157 cache = modest_runtime_get_images_cache ();
3158 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3159 g_free (fidata->cache_id);
3160 g_free (fidata->uri);
3162 if (cache_stream != NULL) {
3163 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3164 tny_stream_close (cache_stream);
3165 g_object_unref (cache_stream);
3168 tny_stream_close (fidata->output_stream);
3169 g_object_unref (fidata->output_stream);
3172 gdk_threads_enter ();
3173 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3174 gdk_threads_leave ();
3180 on_fetch_image (ModestMsgView *msgview,
3183 ModestMsgViewWindow *window)
3185 const gchar *current_account;
3186 ModestMsgViewWindowPrivate *priv;
3187 FetchImageData *fidata;
3189 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3191 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3193 fidata = g_slice_new0 (FetchImageData);
3194 fidata->msg_view = g_object_ref (msgview);
3195 fidata->uri = g_strdup (uri);
3196 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3197 fidata->output_stream = g_object_ref (stream);
3199 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3200 g_object_unref (fidata->output_stream);
3201 g_free (fidata->cache_id);
3202 g_free (fidata->uri);
3203 g_object_unref (fidata->msg_view);
3204 g_slice_free (FetchImageData, fidata);
3205 tny_stream_close (stream);
3213 modest_msg_view_window_add_to_contacts (ModestMsgViewWindow *self)
3215 modest_ui_actions_on_add_to_contacts (NULL, MODEST_WINDOW (self));