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);
781 g_object_unref (header);
784 } else if (cycle && gtk_tree_model_get_iter_first (model, &tmp_iter)) {
785 next = gtk_tree_model_get_path (model, &tmp_iter);
787 /* Ensure that we are not selecting the same */
788 if (gtk_tree_path_compare (path, next) != 0) {
789 gtk_tree_model_get (model, &tmp_iter,
790 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
793 if (msg_is_visible (header, is_outbox)) {
794 *row_reference = gtk_tree_row_reference_new (model, next);
798 g_object_unref (header);
802 /* If we ended up in the same message
803 then there is no valid next
808 /* If there are no more messages and we don't
809 want to start again in the first one then
810 there is no valid next message */
816 gtk_tree_path_free (path);
818 gtk_tree_path_free (next);
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);
1113 check_dimming_rules_after_change_in_idle (gpointer data)
1115 /* The window could have dissapeared */
1116 if (MODEST_IS_WINDOW (data)) {
1117 ModestWindow *win = MODEST_WINDOW (data);
1118 gdk_threads_enter ();
1119 modest_ui_actions_check_menu_dimming_rules (win);
1120 modest_ui_actions_check_toolbar_dimming_rules (win);
1121 gdk_threads_leave ();
1128 check_dimming_rules_after_change (ModestMsgViewWindow *window)
1130 static guint dimming_delayer = 0;
1132 if (dimming_delayer > 0)
1133 g_source_remove (dimming_delayer);
1135 /* We're expecting a lot of changes at the same time so don't
1136 need to check dimming rules for every change that
1138 dimming_delayer = g_timeout_add (100, check_dimming_rules_after_change_in_idle, window);
1142 /* On insertions we check if the folder still has the message we are
1143 * showing or do not. If do not, we do nothing. Which means we are still
1144 * not attached to any header folder and thus next/prev buttons are
1145 * still dimmed. Once the message that is shown by msg-view is found, the
1146 * new model of header-view will be attached and the references will be set.
1147 * On each further insertions dimming rules will be checked. However
1148 * this requires extra CPU time at least works.
1149 * (An message might be deleted from TnyFolder and thus will not be
1150 * inserted into the model again for example if it is removed by the
1151 * imap server and the header view is refreshed.)
1154 modest_msg_view_window_on_row_inserted (GtkTreeModel *model,
1155 GtkTreePath *tree_path,
1156 GtkTreeIter *tree_iter,
1157 ModestMsgViewWindow *window)
1159 ModestMsgViewWindowPrivate *priv = NULL;
1160 TnyHeader *header = NULL;
1162 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1163 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1165 g_assert (model == priv->header_model);
1167 /* Check if the newly inserted message is the same we are actually
1168 * showing. IF not, we should remain detached from the header model
1169 * and thus prev and next toolbar buttons should remain dimmed. */
1170 gtk_tree_model_get (model, tree_iter,
1171 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1174 if (TNY_IS_HEADER (header)) {
1177 uid = modest_tny_folder_get_header_unique_id (header);
1178 if (!g_str_equal(priv->msg_uid, uid)) {
1179 check_dimming_rules_after_change (window);
1181 g_object_unref (G_OBJECT(header));
1185 g_object_unref(G_OBJECT(header));
1188 if (priv->row_reference) {
1189 gtk_tree_row_reference_free (priv->row_reference);
1192 /* Setup row_reference for the actual msg. */
1193 priv->row_reference = gtk_tree_row_reference_new (priv->header_model, tree_path);
1194 if (priv->row_reference == NULL) {
1195 g_warning("No reference for msg header item.");
1199 /* Now set up next_row_reference. */
1200 if (priv->next_row_reference) {
1201 gtk_tree_row_reference_free (priv->next_row_reference);
1204 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1205 select_next_valid_row (priv->header_model,
1206 &(priv->next_row_reference), FALSE, priv->is_outbox);
1208 /* Connect the remaining callbacks to become able to detect
1209 * changes in header-view. */
1210 priv->row_changed_handler =
1211 g_signal_connect (priv->header_model, "row-changed",
1212 G_CALLBACK (modest_msg_view_window_on_row_changed),
1214 priv->row_deleted_handler =
1215 g_signal_connect (priv->header_model, "row-deleted",
1216 G_CALLBACK (modest_msg_view_window_on_row_deleted),
1218 priv->rows_reordered_handler =
1219 g_signal_connect (priv->header_model, "rows-reordered",
1220 G_CALLBACK (modest_msg_view_window_on_row_reordered),
1223 check_dimming_rules_after_change (window);
1227 modest_msg_view_window_on_row_reordered (GtkTreeModel *header_model,
1231 ModestMsgViewWindow *window)
1233 ModestMsgViewWindowPrivate *priv = NULL;
1234 gboolean already_changed = FALSE;
1236 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1238 /* If the current row was reordered select the proper next
1239 valid row. The same if the next row reference changes */
1240 if (priv->row_reference &&
1241 gtk_tree_row_reference_valid (priv->row_reference)) {
1243 path = gtk_tree_row_reference_get_path (priv->row_reference);
1244 if (gtk_tree_path_compare (path, arg1) == 0) {
1245 if (priv->next_row_reference) {
1246 gtk_tree_row_reference_free (priv->next_row_reference);
1248 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1249 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1250 already_changed = TRUE;
1252 gtk_tree_path_free (path);
1254 if (!already_changed &&
1255 priv->next_row_reference &&
1256 gtk_tree_row_reference_valid (priv->next_row_reference)) {
1258 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1259 if (gtk_tree_path_compare (path, arg1) == 0) {
1260 if (priv->next_row_reference) {
1261 gtk_tree_row_reference_free (priv->next_row_reference);
1263 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1264 select_next_valid_row (header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1266 gtk_tree_path_free (path);
1268 check_dimming_rules_after_change (window);
1271 /* The modest_msg_view_window_update_model_replaced implements update
1272 * function for ModestHeaderViewObserver. Checks whether the TnyFolder
1273 * actually belongs to the header-view is the same as the TnyFolder of
1274 * the message of msg-view or not. If they are different, there is
1275 * nothing to do. If they are the same, then the model has replaced and
1276 * the reference in msg-view shall be replaced from the old model to
1277 * the new model. In this case the view will be detached from it's
1278 * header folder. From this point the next/prev buttons are dimmed.
1281 modest_msg_view_window_update_model_replaced (ModestHeaderViewObserver *observer,
1282 GtkTreeModel *model,
1283 const gchar *tny_folder_id)
1285 ModestMsgViewWindowPrivate *priv = NULL;
1286 ModestMsgViewWindow *window = NULL;
1288 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1289 g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer));
1291 window = MODEST_MSG_VIEW_WINDOW(observer);
1292 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window);
1294 /* If there is an other folder in the header-view then we do
1295 * not care about it's model (msg list). Else if the
1296 * header-view shows the folder the msg shown by us is in, we
1297 * shall replace our model reference and make some check. */
1298 if(model == NULL || tny_folder_id == NULL ||
1299 (priv->header_folder_id && !g_str_equal(tny_folder_id, priv->header_folder_id)))
1302 /* Model is changed(replaced), so we should forget the old
1303 * one. Because there might be other references and there
1304 * might be some change on the model even if we unreferenced
1305 * it, we need to disconnect our signals here. */
1306 if (priv->header_model) {
1307 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1308 priv->row_changed_handler))
1309 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1310 priv->row_changed_handler);
1311 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1312 priv->row_deleted_handler))
1313 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1314 priv->row_deleted_handler);
1315 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1316 priv->row_inserted_handler))
1317 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1318 priv->row_inserted_handler);
1319 if (g_signal_handler_is_connected(G_OBJECT (priv->header_model),
1320 priv->rows_reordered_handler))
1321 g_signal_handler_disconnect(G_OBJECT (priv->header_model),
1322 priv->rows_reordered_handler);
1325 if (priv->row_reference)
1326 gtk_tree_row_reference_free (priv->row_reference);
1327 if (priv->next_row_reference)
1328 gtk_tree_row_reference_free (priv->next_row_reference);
1329 g_object_unref(priv->header_model);
1332 priv->row_changed_handler = 0;
1333 priv->row_deleted_handler = 0;
1334 priv->row_inserted_handler = 0;
1335 priv->rows_reordered_handler = 0;
1336 priv->next_row_reference = NULL;
1337 priv->row_reference = NULL;
1338 priv->header_model = NULL;
1341 priv->header_model = g_object_ref (model);
1343 /* Also we must connect to the new model for row insertions.
1344 * Only for insertions now. We will need other ones only after
1345 * the msg is show by msg-view is added to the new model. */
1346 priv->row_inserted_handler =
1347 g_signal_connect (priv->header_model, "row-inserted",
1348 G_CALLBACK(modest_msg_view_window_on_row_inserted),
1351 modest_ui_actions_check_menu_dimming_rules(MODEST_WINDOW(window));
1352 modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window));
1356 modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self)
1358 ModestMsgViewWindowPrivate *priv= NULL;
1360 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
1361 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1363 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
1367 modest_msg_view_window_get_header (ModestMsgViewWindow *self)
1369 ModestMsgViewWindowPrivate *priv= NULL;
1371 TnyHeader *header = NULL;
1372 GtkTreePath *path = NULL;
1375 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL);
1376 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1378 /* If the message was not obtained from a treemodel,
1379 * for instance if it was opened directly by the search UI:
1381 if (priv->header_model == NULL ||
1382 priv->row_reference == NULL ||
1383 !gtk_tree_row_reference_valid (priv->row_reference)) {
1384 msg = modest_msg_view_window_get_message (self);
1386 header = tny_msg_get_header (msg);
1387 g_object_unref (msg);
1392 /* Get iter of the currently selected message in the header view: */
1393 path = gtk_tree_row_reference_get_path (priv->row_reference);
1394 g_return_val_if_fail (path != NULL, NULL);
1395 gtk_tree_model_get_iter (priv->header_model,
1399 /* Get current message header */
1400 gtk_tree_model_get (priv->header_model, &iter,
1401 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1404 gtk_tree_path_free (path);
1409 modest_msg_view_window_get_message (ModestMsgViewWindow *self)
1411 ModestMsgViewWindowPrivate *priv;
1413 g_return_val_if_fail (self, NULL);
1415 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
1417 return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
1421 modest_msg_view_window_get_message_uid (ModestMsgViewWindow *self)
1423 ModestMsgViewWindowPrivate *priv;
1425 g_return_val_if_fail (self, NULL);
1427 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
1429 return (const gchar*) priv->msg_uid;
1433 modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle,
1436 ModestMsgViewWindow *window = MODEST_MSG_VIEW_WINDOW (data);
1437 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1438 ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1442 is_active = gtk_toggle_action_get_active (toggle);
1445 gtk_widget_show (priv->find_toolbar);
1446 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1448 gtk_widget_hide (priv->find_toolbar);
1451 /* update the toggle buttons status */
1452 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage");
1453 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1454 action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu");
1455 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active);
1460 modest_msg_view_window_find_toolbar_close (GtkWidget *widget,
1461 ModestMsgViewWindow *obj)
1463 GtkToggleAction *toggle;
1464 ModestWindowPrivate *parent_priv;
1465 ModestMsgViewWindowPrivate *priv;
1467 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1468 parent_priv = MODEST_WINDOW_GET_PRIVATE (obj);
1470 toggle = GTK_TOGGLE_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"));
1471 gtk_toggle_action_set_active (toggle, FALSE);
1472 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1476 modest_msg_view_window_find_toolbar_search (GtkWidget *widget,
1477 ModestMsgViewWindow *obj)
1479 gchar *current_search;
1480 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj);
1482 if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) {
1483 hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find"));
1487 g_object_get (G_OBJECT (widget), "prefix", ¤t_search, NULL);
1489 if ((current_search == NULL) || (strcmp (current_search, "") == 0)) {
1490 g_free (current_search);
1491 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ecdg_ib_find_rep_enter_text"));
1495 if ((priv->last_search == NULL) || (strcmp (priv->last_search, current_search) != 0)) {
1497 g_free (priv->last_search);
1498 priv->last_search = g_strdup (current_search);
1499 result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view),
1502 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches"));
1503 g_free (priv->last_search);
1504 priv->last_search = NULL;
1506 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1507 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1510 if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) {
1511 hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete"));
1512 g_free (priv->last_search);
1513 priv->last_search = NULL;
1515 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
1516 hildon_find_toolbar_highlight_entry (HILDON_FIND_TOOLBAR (priv->find_toolbar), TRUE);
1520 g_free (current_search);
1525 modest_msg_view_window_set_zoom (ModestWindow *window,
1528 ModestMsgViewWindowPrivate *priv;
1529 ModestWindowPrivate *parent_priv;
1530 GtkAction *action = NULL;
1531 gint int_zoom = (gint) rint (zoom*100.0+0.1);
1533 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
1535 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1536 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1537 modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom);
1539 action = gtk_ui_manager_get_action (parent_priv->ui_manager,
1540 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu");
1542 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom);
1546 modest_msg_view_window_get_zoom (ModestWindow *window)
1548 ModestMsgViewWindowPrivate *priv;
1550 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0);
1552 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1553 return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view));
1557 modest_msg_view_window_zoom_plus (ModestWindow *window)
1559 ModestWindowPrivate *parent_priv;
1560 GtkRadioAction *zoom_radio_action;
1561 GSList *group, *node;
1563 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1564 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1565 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1567 group = gtk_radio_action_get_group (zoom_radio_action);
1569 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (group->data))) {
1570 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_max_zoom_level_reached"));
1574 for (node = group; node != NULL; node = g_slist_next (node)) {
1575 if ((node->next != NULL) && gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->next->data))) {
1576 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->data), TRUE);
1584 modest_msg_view_window_zoom_minus (ModestWindow *window)
1586 ModestWindowPrivate *parent_priv;
1587 GtkRadioAction *zoom_radio_action;
1588 GSList *group, *node;
1590 parent_priv = MODEST_WINDOW_GET_PRIVATE (window);
1591 zoom_radio_action = GTK_RADIO_ACTION (gtk_ui_manager_get_action (parent_priv->ui_manager,
1592 "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"));
1594 group = gtk_radio_action_get_group (zoom_radio_action);
1596 for (node = group; node != NULL; node = g_slist_next (node)) {
1597 if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (node->data))) {
1598 if (node->next != NULL) {
1599 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (node->next->data), TRUE);
1602 hildon_banner_show_information (NULL, NULL, dgettext("hildon-common-strings", "ckct_ib_min_zoom_level_reached"));
1612 modest_msg_view_window_key_event (GtkWidget *window,
1618 focus = gtk_window_get_focus (GTK_WINDOW (window));
1620 /* for the find toolbar case */
1621 if (focus && GTK_IS_ENTRY (focus)) {
1622 if (event->keyval == GDK_BackSpace) {
1624 copy = gdk_event_copy ((GdkEvent *) event);
1625 gtk_widget_event (focus, copy);
1626 gdk_event_free (copy);
1631 if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
1632 event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
1633 event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up ||
1634 event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down ||
1635 event->keyval == GDK_Home || event->keyval == GDK_KP_Home ||
1636 event->keyval == GDK_End || event->keyval == GDK_KP_End) {
1637 /* ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); */
1638 /* gboolean return_value; */
1640 if (event->type == GDK_KEY_PRESS) {
1641 GtkScrollType scroll_type;
1643 switch (event->keyval) {
1646 scroll_type = GTK_SCROLL_STEP_UP; break;
1649 scroll_type = GTK_SCROLL_STEP_DOWN; break;
1651 case GDK_KP_Page_Up:
1652 scroll_type = GTK_SCROLL_PAGE_UP; break;
1654 case GDK_KP_Page_Down:
1655 scroll_type = GTK_SCROLL_PAGE_DOWN; break;
1658 scroll_type = GTK_SCROLL_START; break;
1661 scroll_type = GTK_SCROLL_END; break;
1662 default: scroll_type = GTK_SCROLL_NONE;
1665 /* g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", */
1666 /* scroll_type, FALSE, &return_value); */
1677 modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window)
1680 ModestMsgViewWindowPrivate *priv;
1681 GtkTreeIter tmp_iter;
1682 gboolean is_last_selected;
1684 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1685 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1687 /*if no model (so no rows at all), then virtually we are the last*/
1688 if (!priv->header_model || !priv->row_reference)
1691 path = gtk_tree_row_reference_get_path (priv->row_reference);
1695 is_last_selected = TRUE;
1696 while (is_last_selected) {
1698 gtk_tree_path_next (path);
1699 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1701 gtk_tree_model_get (priv->header_model, &tmp_iter,
1702 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1705 if (msg_is_visible (header, priv->is_outbox))
1706 is_last_selected = FALSE;
1707 g_object_unref(G_OBJECT(header));
1710 gtk_tree_path_free (path);
1711 return is_last_selected;
1715 modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window)
1717 ModestMsgViewWindowPrivate *priv;
1719 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1720 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1722 return priv->header_model != NULL;
1726 modest_msg_view_window_is_search_result (ModestMsgViewWindow *window)
1728 ModestMsgViewWindowPrivate *priv;
1730 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1731 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1733 return priv->is_search_result;
1737 msg_is_visible (TnyHeader *header, gboolean check_outbox)
1739 if ((tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED))
1741 if (!check_outbox) {
1744 ModestTnySendQueueStatus status;
1745 status = modest_tny_all_send_queues_get_msg_status (header);
1746 return ((status != MODEST_TNY_SEND_QUEUE_FAILED) &&
1747 (status != MODEST_TNY_SEND_QUEUE_SENDING));
1752 modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window)
1755 ModestMsgViewWindowPrivate *priv;
1756 gboolean is_first_selected;
1757 GtkTreeIter tmp_iter;
1758 /* gchar * path_string;*/
1760 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE);
1761 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1763 /*if no model (so no rows at all), then virtually we are the first*/
1764 if (!priv->header_model || !priv->row_reference)
1767 path = gtk_tree_row_reference_get_path (priv->row_reference);
1771 /* path_string = gtk_tree_path_to_string (path);
1772 is_first_selected = strcmp (path_string, "0");
1774 g_free (path_string);
1775 gtk_tree_path_free (path);
1777 return is_first_selected;*/
1779 is_first_selected = TRUE;
1780 while (is_first_selected) {
1782 if(!gtk_tree_path_prev (path))
1784 /* Here the 'if' is needless for logic, but let make sure
1785 * iter is valid for gtk_tree_model_get. */
1786 if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path))
1788 gtk_tree_model_get (priv->header_model, &tmp_iter,
1789 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1792 if (msg_is_visible (header, priv->is_outbox))
1793 is_first_selected = FALSE;
1794 g_object_unref(G_OBJECT(header));
1797 gtk_tree_path_free (path);
1798 return is_first_selected;
1803 GtkTreeRowReference *row_reference;
1807 message_reader_performer (gboolean canceled,
1809 GtkWindow *parent_window,
1810 TnyAccount *account,
1813 ModestMailOperation *mail_op = NULL;
1814 MsgReaderInfo *info;
1816 info = (MsgReaderInfo *) user_data;
1817 if (canceled || err) {
1821 /* Register the header - it'll be unregistered in the callback */
1822 modest_window_mgr_register_header (modest_runtime_get_window_mgr (), info->header, NULL);
1824 /* New mail operation */
1825 mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(parent_window),
1826 modest_ui_actions_disk_operations_error_handler,
1829 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op);
1830 modest_mail_operation_get_msg (mail_op, info->header, TRUE, view_msg_cb, info->row_reference);
1831 g_object_unref (mail_op);
1833 /* Update dimming rules */
1834 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (parent_window));
1835 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (parent_window));
1838 /* Frees. The row_reference will be freed by the view_msg_cb callback */
1839 g_object_unref (info->header);
1840 g_slice_free (MsgReaderInfo, info);
1845 * Reads the message whose summary item is @header. It takes care of
1846 * several things, among others:
1848 * If the message was not previously downloaded then ask the user
1849 * before downloading. If there is no connection launch the connection
1850 * dialog. Update toolbar dimming rules.
1852 * Returns: TRUE if the mail operation was started, otherwise if the
1853 * user do not want to download the message, or if the user do not
1854 * want to connect, then the operation is not issued
1857 message_reader (ModestMsgViewWindow *window,
1858 ModestMsgViewWindowPrivate *priv,
1860 GtkTreeRowReference *row_reference)
1862 gboolean already_showing = FALSE;
1863 ModestWindow *msg_window = NULL;
1864 ModestWindowMgr *mgr;
1865 TnyAccount *account;
1867 MsgReaderInfo *info;
1869 g_return_val_if_fail (row_reference != NULL, FALSE);
1871 mgr = modest_runtime_get_window_mgr ();
1872 already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window);
1873 if (already_showing && (msg_window != MODEST_WINDOW (window))) {
1876 gtk_window_present (GTK_WINDOW (msg_window));
1877 g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval);
1881 /* Msg download completed */
1882 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) {
1883 /* Ask the user if he wants to download the message if
1885 if (!tny_device_is_online (modest_runtime_get_device())) {
1886 GtkResponseType response;
1888 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
1889 _("mcen_nc_get_msg"));
1890 if (response == GTK_RESPONSE_CANCEL)
1893 folder = tny_header_get_folder (header);
1894 info = g_slice_new (MsgReaderInfo);
1895 info->header = g_object_ref (header);
1896 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1898 /* Offer the connection dialog if necessary */
1899 modest_platform_connect_if_remote_and_perform ((GtkWindow *) window,
1901 TNY_FOLDER_STORE (folder),
1902 message_reader_performer,
1904 g_object_unref (folder);
1909 folder = tny_header_get_folder (header);
1910 account = tny_folder_get_account (folder);
1911 info = g_slice_new (MsgReaderInfo);
1912 info->header = g_object_ref (header);
1913 info->row_reference = gtk_tree_row_reference_copy (row_reference);
1915 message_reader_performer (FALSE, NULL, (GtkWindow *) window, account, info);
1916 g_object_unref (account);
1917 g_object_unref (folder);
1923 modest_msg_view_window_select_next_message (ModestMsgViewWindow *window)
1925 ModestMsgViewWindowPrivate *priv;
1926 GtkTreePath *path= NULL;
1927 GtkTreeIter tmp_iter;
1929 gboolean retval = TRUE;
1930 GtkTreeRowReference *row_reference = NULL;
1932 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1933 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1935 if (!priv->row_reference)
1938 /* Update the next row reference if it's not valid. This could
1939 happen if for example the header which it was pointing to,
1940 was deleted. The best place to do it is in the row-deleted
1941 handler but the tinymail model do not work like the glib
1942 tree models and reports the deletion when the row is still
1944 if (!gtk_tree_row_reference_valid (priv->next_row_reference)) {
1945 if (gtk_tree_row_reference_valid (priv->row_reference)) {
1946 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
1947 select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE, priv->is_outbox);
1950 if (priv->next_row_reference)
1951 path = gtk_tree_row_reference_get_path (priv->next_row_reference);
1955 row_reference = gtk_tree_row_reference_copy (priv->next_row_reference);
1957 gtk_tree_model_get_iter (priv->header_model,
1960 gtk_tree_path_free (path);
1962 gtk_tree_model_get (priv->header_model, &tmp_iter,
1963 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1966 /* Read the message & show it */
1967 if (!message_reader (window, priv, header, row_reference)) {
1970 gtk_tree_row_reference_free (row_reference);
1973 g_object_unref (header);
1979 modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window)
1981 ModestMsgViewWindowPrivate *priv = NULL;
1983 gboolean finished = FALSE;
1984 gboolean retval = FALSE;
1986 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE);
1987 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
1989 /* Return inmediatly if there is no header model */
1990 if (!priv->header_model || !priv->row_reference)
1993 path = gtk_tree_row_reference_get_path (priv->row_reference);
1994 while (!finished && gtk_tree_path_prev (path)) {
1998 gtk_tree_model_get_iter (priv->header_model, &iter, path);
1999 gtk_tree_model_get (priv->header_model, &iter,
2000 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2004 if (msg_is_visible (header, priv->is_outbox)) {
2005 GtkTreeRowReference *row_reference;
2006 row_reference = gtk_tree_row_reference_new (priv->header_model, path);
2007 /* Read the message & show it */
2008 retval = message_reader (window, priv, header, row_reference);
2009 gtk_tree_row_reference_free (row_reference);
2013 g_object_unref (header);
2017 gtk_tree_path_free (path);
2022 view_msg_cb (ModestMailOperation *mail_op,
2029 ModestMsgViewWindow *self = NULL;
2030 ModestMsgViewWindowPrivate *priv = NULL;
2031 GtkTreeRowReference *row_reference = NULL;
2033 /* Unregister the header (it was registered before creating the mail operation) */
2034 modest_window_mgr_unregister_header (modest_runtime_get_window_mgr (), header);
2036 row_reference = (GtkTreeRowReference *) user_data;
2038 gtk_tree_row_reference_free (row_reference);
2042 /* If there was any error */
2043 if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) {
2044 gtk_tree_row_reference_free (row_reference);
2048 /* Get the window */
2049 self = (ModestMsgViewWindow *) modest_mail_operation_get_source (mail_op);
2050 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2051 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2053 /* Update the row reference */
2054 if (priv->row_reference != NULL) {
2055 gtk_tree_row_reference_free (priv->row_reference);
2056 priv->row_reference = gtk_tree_row_reference_copy (row_reference);
2057 if (priv->next_row_reference != NULL) {
2058 gtk_tree_row_reference_free (priv->next_row_reference);
2060 priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference);
2061 select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE, priv->is_outbox);
2064 /* Mark header as read */
2065 if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN))
2066 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2068 /* Set new message */
2069 if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) {
2070 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2071 modest_msg_view_window_update_priority (self);
2072 update_window_title (MODEST_MSG_VIEW_WINDOW (self));
2073 modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view));
2076 /* Set the new message uid of the window */
2077 if (priv->msg_uid) {
2078 g_free (priv->msg_uid);
2079 priv->msg_uid = modest_tny_folder_get_header_unique_id (header);
2082 /* Notify the observers */
2083 g_signal_emit (G_OBJECT (self), signals[MSG_CHANGED_SIGNAL],
2084 0, priv->header_model, priv->row_reference);
2087 g_object_unref (self);
2088 gtk_tree_row_reference_free (row_reference);
2092 modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window)
2094 ModestMsgViewWindowPrivate *priv;
2096 TnyFolderType folder_type;
2098 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2100 folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2102 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2106 folder = tny_msg_get_folder (msg);
2108 folder_type = modest_tny_folder_guess_folder_type (folder);
2109 g_object_unref (folder);
2111 g_object_unref (msg);
2119 modest_msg_view_window_update_priority (ModestMsgViewWindow *window)
2121 ModestMsgViewWindowPrivate *priv;
2122 TnyHeader *header = NULL;
2123 TnyHeaderFlags flags = 0;
2125 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2127 if (priv->header_model && priv->row_reference) {
2129 GtkTreePath *path = NULL;
2131 path = gtk_tree_row_reference_get_path (priv->row_reference);
2132 g_return_if_fail (path != NULL);
2133 gtk_tree_model_get_iter (priv->header_model,
2135 gtk_tree_row_reference_get_path (priv->row_reference));
2137 gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
2139 gtk_tree_path_free (path);
2142 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2144 header = tny_msg_get_header (msg);
2145 g_object_unref (msg);
2150 flags = tny_header_get_flags (header);
2151 g_object_unref(G_OBJECT(header));
2154 modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags);
2159 toolbar_resize (ModestMsgViewWindow *self)
2161 ModestMsgViewWindowPrivate *priv = NULL;
2162 ModestWindowPrivate *parent_priv = NULL;
2164 gint static_button_size;
2165 ModestWindowMgr *mgr;
2167 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self));
2168 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2169 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2171 mgr = modest_runtime_get_window_mgr ();
2172 static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108;
2174 if (parent_priv->toolbar) {
2175 /* left size buttons */
2176 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply");
2177 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2178 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2179 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2180 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo");
2181 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2182 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2183 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2184 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage");
2185 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2186 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2187 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2188 widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage");
2189 gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE);
2190 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE);
2191 gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1);
2193 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE);
2194 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE);
2195 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2196 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE);
2197 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2198 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE);
2199 gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2200 gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE);
2206 modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata)
2208 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
2209 ModestWindowPrivate *parent_priv;
2210 ModestWindowMgr *mgr;
2211 gboolean is_fullscreen;
2212 GtkAction *fs_toggle_action;
2215 mgr = modest_runtime_get_window_mgr ();
2216 is_fullscreen = (modest_window_mgr_get_fullscreen_mode (mgr))?1:0;
2218 parent_priv = MODEST_WINDOW_GET_PRIVATE (widget);
2220 fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu");
2221 active = (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action)))?1:0;
2222 if (is_fullscreen != active) {
2223 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen);
2225 toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget));
2233 modest_msg_view_window_show_toolbar (ModestWindow *self,
2234 gboolean show_toolbar)
2236 ModestMsgViewWindowPrivate *priv = NULL;
2237 ModestWindowPrivate *parent_priv;
2238 GtkWidget *reply_button = NULL, *menu = NULL;
2239 GtkWidget *placeholder = NULL;
2241 const gchar *action_name;
2244 parent_priv = MODEST_WINDOW_GET_PRIVATE(self);
2245 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2247 /* Set optimized view status */
2248 priv->optimized_view = !show_toolbar;
2250 if (!parent_priv->toolbar) {
2251 parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2253 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2255 priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ());
2256 priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel");
2257 priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext");
2258 priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack");
2259 toolbar_resize (MODEST_MSG_VIEW_WINDOW (self));
2261 /* Add ProgressBar (Transfer toolbar) */
2262 priv->progress_bar = modest_progress_bar_new ();
2263 gtk_widget_set_no_show_all (priv->progress_bar, TRUE);
2264 placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView");
2265 insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder));
2266 gtk_container_add (GTK_CONTAINER (priv->progress_toolitem), priv->progress_bar);
2267 gtk_toolbar_insert(GTK_TOOLBAR(parent_priv->toolbar), GTK_TOOL_ITEM (priv->progress_toolitem), insert_index);
2269 /* Connect cancel 'clicked' signal to abort progress mode */
2270 g_signal_connect(priv->cancel_toolitem, "clicked",
2271 G_CALLBACK(cancel_progressbar),
2274 /* Add it to the observers list */
2275 priv->progress_widgets = g_slist_prepend(priv->progress_widgets, priv->progress_bar);
2278 hildon_window_add_toolbar (HILDON_WINDOW (self),
2279 GTK_TOOLBAR (parent_priv->toolbar));
2281 /* Set reply button tap and hold menu */
2282 reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2283 "/ToolBar/ToolbarMessageReply");
2284 menu = gtk_ui_manager_get_widget (parent_priv->ui_manager,
2285 "/ToolbarReplyCSM");
2286 gtk_widget_tap_and_hold_setup (GTK_WIDGET (reply_button), menu, NULL, 0);
2290 /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */
2291 /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */
2292 gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE);
2294 gtk_widget_show (GTK_WIDGET (parent_priv->toolbar));
2295 if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self)))
2296 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER);
2298 set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL);
2301 gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE);
2302 gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar));
2305 /* Update also the actions (to update the toggles in the
2306 menus), we have to do it manually because some other window
2307 of the same time could have changed it (remember that the
2308 toolbar fullscreen mode is shared by all the windows of the
2310 if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ()))
2311 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu";
2313 action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu";
2315 action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name);
2316 modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action),
2321 modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard,
2323 ModestMsgViewWindow *window)
2325 if (!GTK_WIDGET_VISIBLE (window))
2328 modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD);
2332 modest_msg_view_window_transfer_mode_enabled (ModestMsgViewWindow *self)
2334 ModestMsgViewWindowPrivate *priv;
2336 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE);
2337 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2339 return priv->current_toolbar_mode == TOOLBAR_MODE_TRANSFER;
2343 cancel_progressbar (GtkToolButton *toolbutton,
2344 ModestMsgViewWindow *self)
2347 ModestMsgViewWindowPrivate *priv;
2349 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2351 /* Get operation observers and cancel its current operation */
2352 tmp = priv->progress_widgets;
2354 modest_progress_object_cancel_current_operation (MODEST_PROGRESS_OBJECT(tmp->data));
2355 tmp=g_slist_next(tmp);
2359 observers_empty (ModestMsgViewWindow *self)
2362 ModestMsgViewWindowPrivate *priv;
2363 gboolean is_empty = TRUE;
2364 guint pending_ops = 0;
2366 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self);
2367 tmp = priv->progress_widgets;
2369 /* Check all observers */
2370 while (tmp && is_empty) {
2371 pending_ops = modest_progress_object_num_pending_operations (MODEST_PROGRESS_OBJECT(tmp->data));
2372 is_empty = pending_ops == 0;
2374 tmp = g_slist_next(tmp);
2381 on_account_removed (TnyAccountStore *account_store,
2382 TnyAccount *account,
2385 /* Do nothing if it's a transport account, because we only
2386 show the messages of a store account */
2387 if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) {
2388 const gchar *parent_acc = NULL;
2389 const gchar *our_acc = NULL;
2391 our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data));
2392 parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2394 /* Close this window if I'm showing a message of the removed account */
2395 if (strcmp (parent_acc, our_acc) == 0)
2396 modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data));
2401 on_mail_operation_started (ModestMailOperation *mail_op,
2404 ModestMsgViewWindow *self;
2405 ModestMailOperationTypeOperation op_type;
2407 ModestMsgViewWindowPrivate *priv;
2408 GObject *source = NULL;
2410 self = MODEST_MSG_VIEW_WINDOW (user_data);
2411 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2412 op_type = modest_mail_operation_get_type_operation (mail_op);
2413 tmp = priv->progress_widgets;
2414 source = modest_mail_operation_get_source(mail_op);
2415 if (G_OBJECT (self) == source) {
2416 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2417 set_toolbar_transfer_mode(self);
2419 modest_progress_object_add_operation (
2420 MODEST_PROGRESS_OBJECT (tmp->data),
2422 tmp = g_slist_next (tmp);
2426 g_object_unref (source);
2430 on_mail_operation_finished (ModestMailOperation *mail_op,
2433 ModestMsgViewWindow *self;
2434 ModestMailOperationTypeOperation op_type;
2436 ModestMsgViewWindowPrivate *priv;
2438 self = MODEST_MSG_VIEW_WINDOW (user_data);
2439 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2440 op_type = modest_mail_operation_get_type_operation (mail_op);
2441 tmp = priv->progress_widgets;
2443 if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) {
2445 modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data),
2447 tmp = g_slist_next (tmp);
2450 /* If no more operations are being observed, NORMAL mode is enabled again */
2451 if (observers_empty (self)) {
2452 set_toolbar_mode (self, TOOLBAR_MODE_NORMAL);
2455 /* Update dimming rules. We have to do this right here
2456 and not in view_msg_cb because at that point the
2457 transfer mode is still enabled so the dimming rule
2458 won't let the user delete the message that has been
2459 readed for example */
2460 modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (self));
2461 modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (self));
2466 on_queue_changed (ModestMailOperationQueue *queue,
2467 ModestMailOperation *mail_op,
2468 ModestMailOperationQueueNotification type,
2469 ModestMsgViewWindow *self)
2471 ModestMsgViewWindowPrivate *priv;
2473 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self);
2475 /* If this operations was created by another window, do nothing */
2476 if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self)))
2479 if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) {
2480 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2482 "operation-started",
2483 G_CALLBACK (on_mail_operation_started),
2485 priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
2487 "operation-finished",
2488 G_CALLBACK (on_mail_operation_finished),
2490 } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) {
2491 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2493 "operation-started");
2494 priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers,
2496 "operation-finished");
2501 modest_msg_view_window_get_attachments (ModestMsgViewWindow *win)
2503 ModestMsgViewWindowPrivate *priv;
2504 TnyList *selected_attachments = NULL;
2506 g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL);
2507 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win);
2509 selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2511 return selected_attachments;
2515 modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part)
2517 ModestMsgViewWindowPrivate *priv;
2518 const gchar *msg_uid;
2519 gchar *attachment_uid = NULL;
2520 gint attachment_index = 0;
2521 TnyList *attachments;
2523 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2524 g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL));
2525 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2527 msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window));
2528 attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2529 attachment_index = modest_list_index (attachments, (GObject *) mime_part);
2530 g_object_unref (attachments);
2532 if (msg_uid && attachment_index >= 0) {
2533 attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index);
2536 if (mime_part == NULL) {
2537 gboolean error = FALSE;
2538 TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2539 if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) {
2541 } else if (tny_list_get_length (selected_attachments) > 1) {
2542 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more"));
2546 iter = tny_list_create_iterator (selected_attachments);
2547 mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2548 g_object_unref (iter);
2550 g_object_unref (selected_attachments);
2555 g_object_ref (mime_part);
2558 if (tny_mime_part_is_purged (mime_part)) {
2559 g_object_unref (mime_part);
2563 if (!modest_tny_mime_part_is_msg (mime_part)) {
2564 gchar *filepath = NULL;
2565 const gchar *att_filename = tny_mime_part_get_filename (mime_part);
2566 const gchar *content_type;
2567 gboolean show_error_banner = FALSE;
2569 TnyFsStream *temp_stream = NULL;
2570 temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid,
2573 if (temp_stream != NULL) {
2574 content_type = tny_mime_part_get_content_type (mime_part);
2575 if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) {
2576 /* make the file read-only */
2577 if (g_chmod(filepath, 0444) != 0)
2578 g_warning ("%s: failed to set file '%s' to read-only: %s",
2579 __FUNCTION__, filepath, strerror(errno));
2581 modest_platform_activate_file (filepath, content_type);
2583 /* error while saving attachment, maybe cerm_device_memory_full */
2584 show_error_banner = TRUE;
2586 g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message);
2590 g_object_unref (temp_stream);
2592 /* NOTE: files in the temporary area will be automatically
2593 * cleaned after some time if they are no longer in use */
2595 if (filepath != NULL) {
2596 /* the file may already exist but it isn't writable,
2597 * let's try to open it anyway */
2598 content_type = tny_mime_part_get_content_type (mime_part);
2599 modest_platform_activate_file (filepath, content_type);
2602 g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__);
2603 show_error_banner = TRUE;
2606 if (show_error_banner)
2607 modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed"));
2609 /* message attachment */
2610 TnyHeader *header = NULL;
2611 ModestWindowMgr *mgr;
2612 ModestWindow *msg_win = NULL;
2615 header = tny_msg_get_header (TNY_MSG (mime_part));
2616 mgr = modest_runtime_get_window_mgr ();
2617 found = modest_window_mgr_find_registered_header (mgr, header, &msg_win);
2620 if (msg_win) /* there is already a window for this uid; top it */
2621 gtk_window_present (GTK_WINDOW(msg_win));
2623 /* if it's found, but there is no msg_win, it's probably in the process of being created;
2624 * thus, we don't do anything */
2625 g_warning ("window for is already being created");
2627 /* it's not found, so create a new window for it */
2628 modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */
2629 gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window)));
2631 account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ());
2632 msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid);
2633 modest_window_set_zoom (MODEST_WINDOW (msg_win),
2634 modest_window_get_zoom (MODEST_WINDOW (window)));
2635 modest_window_mgr_register_window (mgr, msg_win);
2636 gtk_widget_show_all (GTK_WIDGET (msg_win));
2639 g_object_unref (mime_part);
2652 GnomeVFSResult result;
2655 static void save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct);
2656 static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info);
2657 static gpointer save_mime_part_to_file (SaveMimePartInfo *info);
2658 static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info);
2661 save_mime_part_info_free (SaveMimePartInfo *info, gboolean with_struct)
2665 for (node = info->pairs; node != NULL; node = g_list_next (node)) {
2666 SaveMimePartPair *pair = (SaveMimePartPair *) node->data;
2667 g_free (pair->filename);
2668 g_object_unref (pair->part);
2669 g_slice_free (SaveMimePartPair, pair);
2671 g_list_free (info->pairs);
2674 gtk_widget_destroy (info->banner);
2675 g_slice_free (SaveMimePartInfo, info);
2680 idle_save_mime_part_show_result (SaveMimePartInfo *info)
2682 if (info->pairs != NULL) {
2683 save_mime_part_to_file (info);
2685 /* This is a GDK lock because we are an idle callback and
2686 * hildon_banner_show_information is or does Gtk+ code */
2688 gdk_threads_enter (); /* CHECKED */
2689 save_mime_part_info_free (info, TRUE);
2690 if (info->result == GNOME_VFS_OK) {
2691 hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved"));
2692 } else if (info->result == GNOME_VFS_ERROR_NO_SPACE) {
2693 hildon_banner_show_information (NULL, NULL, dgettext("ke-recv",
2694 "cerm_device_memory_full"));
2696 hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed"));
2698 gdk_threads_leave (); /* CHECKED */
2705 save_mime_part_to_file (SaveMimePartInfo *info)
2707 GnomeVFSHandle *handle;
2709 SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data;
2711 info->result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644);
2712 if (info->result == GNOME_VFS_OK) {
2713 stream = tny_vfs_stream_new (handle);
2714 if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) {
2715 info->result = GNOME_VFS_ERROR_IO;
2717 g_object_unref (G_OBJECT (stream));
2718 g_object_unref (pair->part);
2719 g_slice_free (SaveMimePartPair, pair);
2720 info->pairs = g_list_delete_link (info->pairs, info->pairs);
2722 save_mime_part_info_free (info, FALSE);
2725 g_idle_add ((GSourceFunc) idle_save_mime_part_show_result, info);
2730 save_mime_parts_to_file_with_checks (SaveMimePartInfo *info)
2732 gboolean is_ok = TRUE;
2733 gint replaced_files = 0;
2734 const GList *files = info->pairs;
2737 for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) {
2738 SaveMimePartPair *pair = iter->data;
2739 if (modest_utils_file_exists (pair->filename)) {
2743 if (replaced_files) {
2744 GtkWidget *confirm_overwrite_dialog;
2745 const gchar *message = (replaced_files == 1) ?
2746 _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple");
2747 confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message);
2748 if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) {
2751 gtk_widget_destroy (confirm_overwrite_dialog);
2755 save_mime_part_info_free (info, TRUE);
2757 GtkWidget *banner = hildon_banner_show_animation (NULL, NULL,
2758 _CS("sfil_ib_saving"));
2759 info->banner = banner;
2760 g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL);
2767 modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts)
2769 ModestMsgViewWindowPrivate *priv;
2770 GList *files_to_save = NULL;
2771 GtkWidget *save_dialog = NULL;
2772 gchar *folder = NULL;
2773 gchar *filename = NULL;
2774 gchar *save_multiple_str = NULL;
2776 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2777 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2779 if (mime_parts == NULL) {
2780 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2781 if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0)
2784 g_object_ref (mime_parts);
2787 /* prepare dialog */
2788 if (tny_list_get_length (mime_parts) == 1) {
2790 /* only one attachment selected */
2791 iter = tny_list_create_iterator (mime_parts);
2792 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2793 g_object_unref (iter);
2794 if (!modest_tny_mime_part_is_msg (mime_part) &&
2795 modest_tny_mime_part_is_attachment_for_modest (mime_part) &&
2796 !tny_mime_part_is_purged (mime_part)) {
2797 filename = g_strdup (tny_mime_part_get_filename (mime_part));
2799 /* TODO: show any error? */
2800 g_warning ("Tried to save a non-file attachment");
2801 g_object_unref (mime_parts);
2804 g_object_unref (mime_part);
2806 save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"),
2807 tny_list_get_length (mime_parts));
2810 save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window),
2811 GTK_FILE_CHOOSER_ACTION_SAVE);
2814 folder = g_build_filename (g_get_home_dir (), DEFAULT_FOLDER, NULL);
2815 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (save_dialog), folder);
2820 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (save_dialog),
2825 /* if multiple, set multiple string */
2826 if (save_multiple_str) {
2827 g_object_set (G_OBJECT (save_dialog), "save-multiple", save_multiple_str, NULL);
2828 gtk_window_set_title (GTK_WINDOW (save_dialog), _FM("sfil_ti_save_objects_files"));
2832 if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) {
2833 gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog));
2835 if (!modest_utils_folder_writable (chooser_uri)) {
2836 hildon_banner_show_information
2837 (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location"));
2841 iter = tny_list_create_iterator (mime_parts);
2842 while (!tny_iterator_is_done (iter)) {
2843 TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter);
2845 if ((modest_tny_mime_part_is_attachment_for_modest (mime_part)) &&
2846 !tny_mime_part_is_purged (mime_part) &&
2847 (tny_mime_part_get_filename (mime_part) != NULL)) {
2848 SaveMimePartPair *pair;
2850 pair = g_slice_new0 (SaveMimePartPair);
2851 if (save_multiple_str) {
2852 gchar *escaped = gnome_vfs_escape_slashes (
2853 tny_mime_part_get_filename (mime_part));
2854 pair->filename = g_build_filename (chooser_uri, escaped, NULL);
2857 pair->filename = g_strdup (chooser_uri);
2859 pair->part = mime_part;
2860 files_to_save = g_list_prepend (files_to_save, pair);
2862 tny_iterator_next (iter);
2864 g_object_unref (iter);
2866 g_free (chooser_uri);
2869 gtk_widget_destroy (save_dialog);
2871 g_object_unref (mime_parts);
2873 if (files_to_save != NULL) {
2874 SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo);
2875 info->pairs = files_to_save;
2876 info->result = TRUE;
2877 save_mime_parts_to_file_with_checks (info);
2882 show_remove_attachment_information (gpointer userdata)
2884 ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata;
2885 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2887 /* We're outside the main lock */
2888 gdk_threads_enter ();
2890 if (priv->remove_attachment_banner != NULL) {
2891 gtk_widget_destroy (priv->remove_attachment_banner);
2892 g_object_unref (priv->remove_attachment_banner);
2895 priv->remove_attachment_banner = g_object_ref (
2896 hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment")));
2898 gdk_threads_leave ();
2904 modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all)
2906 ModestMsgViewWindowPrivate *priv;
2907 TnyList *mime_parts = NULL;
2908 gchar *confirmation_message;
2913 /* TnyFolder *folder; */
2915 g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window));
2916 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
2919 mime_parts = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view));
2921 mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view));
2923 /* Remove already purged messages from mime parts list */
2924 iter = tny_list_create_iterator (mime_parts);
2925 while (!tny_iterator_is_done (iter)) {
2926 TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter));
2927 tny_iterator_next (iter);
2928 if (tny_mime_part_is_purged (part)) {
2929 tny_list_remove (mime_parts, (GObject *) part);
2931 g_object_unref (part);
2933 g_object_unref (iter);
2935 if (tny_list_get_length (mime_parts) == 0) {
2936 g_object_unref (mime_parts);
2940 n_attachments = tny_list_get_length (mime_parts);
2941 if (n_attachments == 1) {
2945 iter = tny_list_create_iterator (mime_parts);
2946 part = (TnyMimePart *) tny_iterator_get_current (iter);
2947 g_object_unref (iter);
2948 if (modest_tny_mime_part_is_msg (part)) {
2950 header = tny_msg_get_header (TNY_MSG (part));
2951 filename = tny_header_dup_subject (header);
2952 g_object_unref (header);
2953 if (filename == NULL)
2954 filename = g_strdup (_("mail_va_no_subject"));
2956 filename = g_strdup (tny_mime_part_get_filename (TNY_MIME_PART (part)));
2958 confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename);
2960 g_object_unref (part);
2962 confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text",
2963 "mcen_nc_purge_files_text",
2964 n_attachments), n_attachments);
2966 response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window),
2967 confirmation_message);
2968 g_free (confirmation_message);
2970 if (response != GTK_RESPONSE_OK) {
2971 g_object_unref (mime_parts);
2975 priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window);
2976 /* folder = tny_msg_get_folder (msg); */
2977 /* tny_msg_uncache_attachments (msg); */
2978 /* tny_folder_refresh (folder, NULL); */
2979 /* g_object_unref (folder); */
2981 iter = tny_list_create_iterator (mime_parts);
2982 while (!tny_iterator_is_done (iter)) {
2985 part = (TnyMimePart *) tny_iterator_get_current (iter);
2986 tny_mime_part_set_purged (TNY_MIME_PART (part));
2987 /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */
2988 g_object_unref (part);
2989 tny_iterator_next (iter);
2991 g_object_unref (iter);
2993 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
2994 tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view));
2995 tny_msg_rewrite_cache (msg);
2996 tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg);
2997 g_object_unref (msg);
2999 g_object_unref (mime_parts);
3001 if (priv->purge_timeout > 0) {
3002 g_source_remove (priv->purge_timeout);
3003 priv->purge_timeout = 0;
3006 if (priv->remove_attachment_banner) {
3007 gtk_widget_destroy (priv->remove_attachment_banner);
3008 g_object_unref (priv->remove_attachment_banner);
3009 priv->remove_attachment_banner = NULL;
3017 update_window_title (ModestMsgViewWindow *window)
3019 ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3021 TnyHeader *header = NULL;
3022 gchar *subject = NULL;
3024 msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view));
3027 header = tny_msg_get_header (msg);
3028 subject = tny_header_dup_subject (header);
3029 g_object_unref (header);
3030 g_object_unref (msg);
3033 if ((subject == NULL)||(subject[0] == '\0')) {
3035 subject = g_strdup (_("mail_va_no_subject"));
3038 gtk_window_set_title (GTK_WINDOW (window), subject);
3042 static void on_move_focus (GtkWidget *widget,
3043 GtkDirectionType direction,
3046 g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus");
3050 fetch_image_open_stream (TnyStreamCache *self, gint64 *expected_size, gchar *uri)
3052 GnomeVFSResult result;
3053 GnomeVFSHandle *handle = NULL;
3054 GnomeVFSFileInfo *info = NULL;
3057 result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
3058 if (result != GNOME_VFS_OK) {
3063 info = gnome_vfs_file_info_new ();
3064 result = gnome_vfs_get_file_info_from_handle (handle, info, GNOME_VFS_FILE_INFO_DEFAULT);
3065 if (result != GNOME_VFS_OK || ! (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)) {
3066 /* We put a "safe" default size for going to cache */
3067 *expected_size = (300*1024);
3069 *expected_size = info->size;
3071 gnome_vfs_file_info_unref (info);
3073 stream = tny_vfs_stream_new (handle);
3082 TnyStream *output_stream;
3083 GtkWidget *msg_view;
3087 on_fetch_image_idle_refresh_view (gpointer userdata)
3090 FetchImageData *fidata = (FetchImageData *) userdata;
3091 g_message ("REFRESH VIEW");
3092 if (GTK_WIDGET_DRAWABLE (fidata->msg_view)) {
3093 g_message ("QUEUING DRAW");
3094 gtk_widget_queue_draw (fidata->msg_view);
3096 g_object_unref (fidata->msg_view);
3097 g_slice_free (FetchImageData, fidata);
3102 on_fetch_image_thread (gpointer userdata)
3104 FetchImageData *fidata = (FetchImageData *) userdata;
3105 TnyStreamCache *cache;
3106 TnyStream *cache_stream;
3108 cache = modest_runtime_get_images_cache ();
3109 cache_stream = tny_stream_cache_get_stream (cache, fidata->cache_id, (TnyStreamCacheOpenStreamFetcher) fetch_image_open_stream, (gpointer) fidata->uri);
3110 g_free (fidata->cache_id);
3111 g_free (fidata->uri);
3113 if (cache_stream != NULL) {
3114 tny_stream_write_to_stream (cache_stream, fidata->output_stream);
3115 tny_stream_close (cache_stream);
3116 g_object_unref (cache_stream);
3119 tny_stream_close (fidata->output_stream);
3120 g_object_unref (fidata->output_stream);
3123 gdk_threads_enter ();
3124 g_idle_add (on_fetch_image_idle_refresh_view, fidata);
3125 gdk_threads_leave ();
3131 on_fetch_image (ModestMsgView *msgview,
3134 ModestMsgViewWindow *window)
3136 const gchar *current_account;
3137 ModestMsgViewWindowPrivate *priv;
3138 FetchImageData *fidata;
3140 priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window);
3142 current_account = modest_window_get_active_account (MODEST_WINDOW (window));
3144 fidata = g_slice_new0 (FetchImageData);
3145 fidata->msg_view = g_object_ref (msgview);
3146 fidata->uri = g_strdup (uri);
3147 fidata->cache_id = modest_images_cache_get_id (current_account, uri);
3148 fidata->output_stream = g_object_ref (stream);
3150 if (g_thread_create (on_fetch_image_thread, fidata, FALSE, NULL) == NULL) {
3151 g_object_unref (fidata->output_stream);
3152 g_free (fidata->cache_id);
3153 g_free (fidata->uri);
3154 g_object_unref (fidata->msg_view);
3155 g_slice_free (FetchImageData, fidata);
3156 tny_stream_close (stream);