X-Git-Url: http://git.maemo.org/git/?p=modest;a=blobdiff_plain;f=src%2Fmaemo%2Fmodest-msg-view-window.c;h=08c65d5bfc0f3b35bdc38e3c6f123db1bbdaf915;hp=3a5f47e592c1488bdd65ac4b7bf98a6f8bbd7f96;hb=0157fe7291bed654b46a5efeff4b9231b6b8a916;hpb=f9300629125062bd4d908bd1211a8b8f697adecf diff --git a/src/maemo/modest-msg-view-window.c b/src/maemo/modest-msg-view-window.c index 3a5f47e..08c65d5 100644 --- a/src/maemo/modest-msg-view-window.c +++ b/src/maemo/modest-msg-view-window.c @@ -35,10 +35,10 @@ #include #include "modest-marshal.h" #include "modest-platform.h" +#include #include #include #include -#include #include #include "modest-msg-view-window-ui-dimming.h" #include @@ -47,18 +47,24 @@ #include #include #include -#include "modest-progress-bar-widget.h" +#include "modest-progress-bar.h" #include "modest-defs.h" #include "modest-hildon-includes.h" -#include #include "modest-ui-dimming-manager.h" #include #include +#include +#include +#include +#include +#include #define DEFAULT_FOLDER "MyDocs/.documents" static void modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass); static void modest_msg_view_window_init (ModestMsgViewWindow *obj); +static void modest_header_view_observer_init( + ModestHeaderViewObserverIface *iface_class); static void modest_msg_view_window_finalize (GObject *obj); static void modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *obj, gpointer data); @@ -67,19 +73,18 @@ static void modest_msg_view_window_find_toolbar_close (GtkWidget *widget, static void modest_msg_view_window_find_toolbar_search (GtkWidget *widget, ModestMsgViewWindow *obj); -static void modest_msg_view_window_set_zoom (ModestWindow *window, - gdouble zoom); +static void modest_msg_view_window_disconnect_signals (ModestWindow *self); +static void modest_msg_view_window_set_zoom (ModestWindow *window, + gdouble zoom); static gdouble modest_msg_view_window_get_zoom (ModestWindow *window); static gboolean modest_msg_view_window_zoom_minus (ModestWindow *window); static gboolean modest_msg_view_window_zoom_plus (ModestWindow *window); -static gboolean modest_msg_view_window_key_release_event (GtkWidget *window, - GdkEventKey *event, - gpointer userdata); +static gboolean modest_msg_view_window_key_event (GtkWidget *window, + GdkEventKey *event, + gpointer userdata); static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata); -static void modest_msg_view_window_scroll_up (ModestWindow *window); -static void modest_msg_view_window_scroll_down (ModestWindow *window); static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window); static void modest_msg_view_window_show_toolbar (ModestWindow *window, @@ -88,27 +93,67 @@ static void modest_msg_view_window_show_toolbar (ModestWindow *window, static void modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard, GdkEvent *event, ModestMsgViewWindow *window); +void modest_msg_view_window_on_row_changed( + GtkTreeModel *header_model, + GtkTreePath *arg1, + GtkTreeIter *arg2, + ModestMsgViewWindow *window); + +void modest_msg_view_window_on_row_deleted( + GtkTreeModel *header_model, + GtkTreePath *arg1, + ModestMsgViewWindow *window); + +void modest_msg_view_window_on_row_inserted( + GtkTreeModel *header_model, + GtkTreePath *tree_path, + GtkTreeIter *tree_iter, + ModestMsgViewWindow *window); + +void modest_msg_view_window_on_row_reordered( + GtkTreeModel *header_model, + GtkTreePath *arg1, + GtkTreeIter *arg2, + gpointer arg3, + ModestMsgViewWindow *window); + +void modest_msg_view_window_update_model_replaced( + ModestHeaderViewObserver *window, + GtkTreeModel *model, + const gchar *tny_folder_id); + +static void cancel_progressbar (GtkToolButton *toolbutton, + ModestMsgViewWindow *self); + +static void on_queue_changed (ModestMailOperationQueue *queue, + ModestMailOperation *mail_op, + ModestMailOperationQueueNotification type, + ModestMsgViewWindow *self); + +static void on_account_removed (TnyAccountStore *account_store, + TnyAccount *account, + gpointer user_data); + +static void on_move_focus (GtkWidget *widget, + GtkDirectionType direction, + gpointer userdata); + +static void view_msg_cb (ModestMailOperation *mail_op, + TnyHeader *header, + gboolean canceled, + TnyMsg *msg, + GError *error, + gpointer user_data); + +static void set_toolbar_mode (ModestMsgViewWindow *self, + ModestToolBarModes mode); -static void cancel_progressbar (GtkToolButton *toolbutton, - ModestMsgViewWindow *self); - -static void on_queue_changed (ModestMailOperationQueue *queue, - ModestMailOperation *mail_op, - ModestMailOperationQueueNotification type, - ModestMsgViewWindow *self); -static void on_account_removed (ModestAccountMgr *obj, - const gchar *account, - gboolean server_account, - gpointer user_data); - -static void view_msg_cb (ModestMailOperation *mail_op, TnyHeader *header, TnyMsg *msg, gpointer user_data); - -static void set_toolbar_mode (ModestMsgViewWindow *self, - ModestToolBarModes mode); +static void update_window_title (ModestMsgViewWindow *window); static gboolean set_toolbar_transfer_mode (ModestMsgViewWindow *self); +static void init_window (ModestMsgViewWindow *obj); -static void update_window_title (ModestMsgViewWindow *window); +static gboolean msg_is_visible (TnyHeader *header, gboolean check_outbox); /* list my signals */ @@ -119,7 +164,7 @@ enum { static const GtkToggleActionEntry msg_view_toggle_action_entries [] = { { "FindInMessage", MODEST_TOOLBAR_ICON_FIND, N_("qgn_toolb_gene_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE }, - { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), NULL, NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE }, + { "ToolsFindInMessage", NULL, N_("mcen_me_viewer_find"), "F", NULL, G_CALLBACK (modest_msg_view_window_toggle_find_toolbar), FALSE }, }; static const GtkRadioActionEntry msg_view_zoom_action_entries [] = { @@ -153,17 +198,37 @@ struct _ModestMsgViewWindowPrivate { /* Optimized view enabled */ gboolean optimized_view; + /* Whether this was created via the *_new_for_search_result() function. */ + gboolean is_search_result; + + /* Whether the message is in outbox */ + gboolean is_outbox; + + /* A reference to the @model of the header view + * to allow selecting previous/next messages, + * if the message is currently selected in the header view. + */ + const gchar *header_folder_id; GtkTreeModel *header_model; GtkTreeRowReference *row_reference; GtkTreeRowReference *next_row_reference; - guint clipboard_change_handler; - guint queue_change_handler; - guint account_removed_handler; + gulong clipboard_change_handler; + gulong queue_change_handler; + gulong account_removed_handler; + gulong row_changed_handler; + gulong row_deleted_handler; + gulong row_inserted_handler; + gulong rows_reordered_handler; + + guint purge_timeout; + GtkWidget *remove_attachment_banner; guint progress_bar_timeout; gchar *msg_uid; + + GSList *sighandlers; }; #define MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ @@ -195,6 +260,17 @@ modest_msg_view_window_get_type (void) my_type = g_type_register_static (MODEST_TYPE_WINDOW, "ModestMsgViewWindow", &my_info, 0); + + static const GInterfaceInfo modest_header_view_observer_info = + { + (GInterfaceInitFunc) modest_header_view_observer_init, + NULL, /* interface_finalize */ + NULL /* interface_data */ + }; + + g_type_add_interface_static (my_type, + MODEST_TYPE_HEADER_VIEW_OBSERVER, + &modest_header_view_observer_info); } return my_type; } @@ -211,7 +287,20 @@ save_state (ModestWindow *self) static void restore_settings (ModestMsgViewWindow *self) { - modest_widget_memory_restore (modest_runtime_get_conf (), + ModestConf *conf; + ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (self); + GtkAction *action; + + conf = modest_runtime_get_conf (); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu"); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), + modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR, NULL)); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu"); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), + modest_conf_get_bool (conf, MODEST_CONF_MSG_VIEW_WINDOW_SHOW_TOOLBAR_FULLSCREEN, NULL)); + modest_widget_memory_restore (conf, G_OBJECT(self), MODEST_CONF_MSG_VIEW_WINDOW_KEY); } @@ -232,6 +321,7 @@ modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass) modest_window_class->zoom_minus_func = modest_msg_view_window_zoom_minus; modest_window_class->zoom_plus_func = modest_msg_view_window_zoom_plus; modest_window_class->show_toolbar_func = modest_msg_view_window_show_toolbar; + modest_window_class->disconnect_signals_func = modest_msg_view_window_disconnect_signals; g_type_class_add_private (gobject_class, sizeof(ModestMsgViewWindowPrivate)); @@ -247,22 +337,103 @@ modest_msg_view_window_class_init (ModestMsgViewWindowClass *klass) G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER); } +static void modest_header_view_observer_init( + ModestHeaderViewObserverIface *iface_class) +{ + iface_class->update_func = modest_msg_view_window_update_model_replaced; +} + static void modest_msg_view_window_init (ModestMsgViewWindow *obj) { ModestMsgViewWindowPrivate *priv; + ModestWindowPrivate *parent_priv = NULL; + GtkActionGroup *action_group = NULL; + GError *error = NULL; + GdkPixbuf *window_icon; + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj); + parent_priv = MODEST_WINDOW_GET_PRIVATE(obj); + parent_priv->ui_manager = gtk_ui_manager_new(); + + action_group = gtk_action_group_new ("ModestMsgViewWindowActions"); + gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE); + + /* Add common actions */ + gtk_action_group_add_actions (action_group, + modest_action_entries, + G_N_ELEMENTS (modest_action_entries), + obj); + gtk_action_group_add_toggle_actions (action_group, + modest_toggle_action_entries, + G_N_ELEMENTS (modest_toggle_action_entries), + obj); + gtk_action_group_add_toggle_actions (action_group, + msg_view_toggle_action_entries, + G_N_ELEMENTS (msg_view_toggle_action_entries), + obj); + gtk_action_group_add_radio_actions (action_group, + msg_view_zoom_action_entries, + G_N_ELEMENTS (msg_view_zoom_action_entries), + 100, + G_CALLBACK (modest_ui_actions_on_change_zoom), + obj); + + gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0); + g_object_unref (action_group); + + /* Load the UI definition */ + gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml", + &error); + if (error) { + g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message); + g_error_free (error); + error = NULL; + } + /* ****** */ + + /* Add accelerators */ + gtk_window_add_accel_group (GTK_WINDOW (obj), + gtk_ui_manager_get_accel_group (parent_priv->ui_manager)); + + priv->is_search_result = FALSE; + priv->is_outbox = FALSE; priv->msg_view = NULL; priv->header_model = NULL; + priv->header_folder_id = NULL; priv->clipboard_change_handler = 0; priv->queue_change_handler = 0; priv->account_removed_handler = 0; + priv->row_changed_handler = 0; + priv->row_deleted_handler = 0; + priv->row_inserted_handler = 0; + priv->rows_reordered_handler = 0; priv->current_toolbar_mode = TOOLBAR_MODE_NORMAL; priv->optimized_view = FALSE; priv->progress_bar_timeout = 0; + priv->purge_timeout = 0; + priv->remove_attachment_banner = NULL; priv->msg_uid = NULL; + + priv->sighandlers = NULL; + + /* Init window */ + init_window (MODEST_MSG_VIEW_WINDOW(obj)); + + /* Set window icon */ + window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON, MODEST_ICON_SIZE_BIG); + if (window_icon) { + gtk_window_set_icon (GTK_WINDOW (obj), window_icon); + g_object_unref (window_icon); + } + + hildon_program_add_window (hildon_program_get_instance(), + HILDON_WINDOW(obj)); + + modest_window_mgr_register_help_id (modest_runtime_get_window_mgr(), + GTK_WINDOW(obj),"applications_email_viewer"); } @@ -306,29 +477,23 @@ set_toolbar_mode (ModestMsgViewWindow *self, switch (mode) { case TOOLBAR_MODE_NORMAL: -/* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */ -/* gtk_action_set_sensitive (widget, TRUE); */ -/* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */ -/* gtk_action_set_sensitive (widget, TRUE); */ -/* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */ -/* gtk_action_set_sensitive (widget, TRUE); */ -/* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */ -/* gtk_action_set_sensitive (widget, TRUE); */ - - if (priv->prev_toolitem) - gtk_widget_show (priv->prev_toolitem); - - if (priv->next_toolitem) - gtk_widget_show (priv->next_toolitem); - - if (priv->progress_toolitem) + if (priv->progress_toolitem) { gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE); + gtk_widget_hide (priv->progress_toolitem); + } + if (priv->progress_bar) gtk_widget_hide (priv->progress_bar); if (priv->cancel_toolitem) gtk_widget_hide (priv->cancel_toolitem); + if (priv->prev_toolitem) + gtk_widget_show (priv->prev_toolitem); + + if (priv->next_toolitem) + gtk_widget_show (priv->next_toolitem); + /* Hide toolbar if optimized view is enabled */ if (priv->optimized_view) { gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE); @@ -337,25 +502,19 @@ set_toolbar_mode (ModestMsgViewWindow *self, break; case TOOLBAR_MODE_TRANSFER: -/* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); */ -/* gtk_action_set_sensitive (widget, FALSE); */ -/* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); */ -/* gtk_action_set_sensitive (widget, FALSE); */ -/* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); */ -/* gtk_action_set_sensitive (widget, FALSE); */ -/* widget = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); */ -/* gtk_action_set_sensitive (widget, FALSE); */ - if (priv->prev_toolitem) gtk_widget_hide (priv->prev_toolitem); if (priv->next_toolitem) gtk_widget_hide (priv->next_toolitem); - if (priv->progress_toolitem) - gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE); if (priv->progress_bar) gtk_widget_show (priv->progress_bar); + + if (priv->progress_toolitem) { + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE); + gtk_widget_show (priv->progress_toolitem); + } if (priv->cancel_toolitem) gtk_widget_show (priv->cancel_toolitem); @@ -374,70 +533,104 @@ set_toolbar_mode (ModestMsgViewWindow *self, } -static GtkWidget * -menubar_to_menu (GtkUIManager *ui_manager) -{ - GtkWidget *main_menu; - GtkWidget *menubar; - GList *iter; - - /* Create new main menu */ - main_menu = gtk_menu_new(); - - /* Get the menubar from the UI manager */ - menubar = gtk_ui_manager_get_widget (ui_manager, "/MenuBar"); - - iter = gtk_container_get_children (GTK_CONTAINER (menubar)); - while (iter) { - GtkWidget *menu; - - menu = GTK_WIDGET (iter->data); - gtk_widget_reparent(menu, main_menu); - - iter = g_list_next (iter); - } - return main_menu; -} - static void -init_window (ModestMsgViewWindow *obj, TnyMsg *msg) +init_window (ModestMsgViewWindow *obj) { GtkWidget *main_vbox; ModestMsgViewWindowPrivate *priv; - ModestWindowPrivate *parent_priv; - + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj); - parent_priv = MODEST_WINDOW_GET_PRIVATE(obj); - priv->msg_view = modest_msg_view_new (msg); + priv->msg_view = GTK_WIDGET (tny_platform_factory_new_msg_view (modest_tny_platform_factory_get_instance ())); modest_msg_view_set_shadow_type (MODEST_MSG_VIEW (priv->msg_view), GTK_SHADOW_NONE); main_vbox = gtk_vbox_new (FALSE, 6); - /* Menubar */ - parent_priv->menubar = menubar_to_menu (parent_priv->ui_manager); - gtk_widget_show_all (GTK_WIDGET(parent_priv->menubar)); - hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar)); - +#ifdef MODEST_USE_MOZEMBED + priv->main_scroll = priv->msg_view; + gtk_widget_set_size_request (priv->msg_view, -1, 1600); +#else priv->main_scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view); +#endif gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->main_scroll), GTK_SHADOW_NONE); modest_maemo_set_thumbable_scrollbar (GTK_SCROLLED_WINDOW(priv->main_scroll), TRUE); - gtk_container_add (GTK_CONTAINER (priv->main_scroll), priv->msg_view); gtk_box_pack_start (GTK_BOX(main_vbox), priv->main_scroll, TRUE, TRUE, 0); gtk_container_add (GTK_CONTAINER(obj), main_vbox); priv->find_toolbar = hildon_find_toolbar_new (NULL); hildon_window_add_toolbar (HILDON_WINDOW (obj), GTK_TOOLBAR (priv->find_toolbar)); gtk_widget_set_no_show_all (priv->find_toolbar, TRUE); - g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj); - g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj); - 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); gtk_widget_show_all (GTK_WIDGET(main_vbox)); +} -} +static void +modest_msg_view_window_disconnect_signals (ModestWindow *self) +{ + ModestMsgViewWindowPrivate *priv; + ModestHeaderView *header_view = NULL; + ModestWindow *main_window = NULL; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + + if (gtk_clipboard_get (GDK_SELECTION_PRIMARY) && + g_signal_handler_is_connected (gtk_clipboard_get (GDK_SELECTION_PRIMARY), + priv->clipboard_change_handler)) + g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), + priv->clipboard_change_handler); + + if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_mail_operation_queue ()), + priv->queue_change_handler)) + g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), + priv->queue_change_handler); + if (g_signal_handler_is_connected (G_OBJECT (modest_runtime_get_account_store ()), + priv->account_removed_handler)) + g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_store ()), + priv->account_removed_handler); + + if (priv->header_model) { + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->row_changed_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->row_changed_handler); + + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->row_deleted_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->row_deleted_handler); + + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->row_inserted_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->row_inserted_handler); + + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->rows_reordered_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->rows_reordered_handler); + } + + modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers); + priv->sighandlers = NULL; + + main_window = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), + FALSE); /* don't create */ + if (!main_window) + return; + + header_view = MODEST_HEADER_VIEW( + modest_main_window_get_child_widget( + MODEST_MAIN_WINDOW(main_window), + MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW)); + if (header_view == NULL) + return; + + modest_header_view_remove_observer(header_view, + MODEST_HEADER_VIEW_OBSERVER(self)); +} static void modest_msg_view_window_finalize (GObject *obj) @@ -445,18 +638,11 @@ modest_msg_view_window_finalize (GObject *obj) ModestMsgViewWindowPrivate *priv; priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj); - if (priv->clipboard_change_handler > 0) { - g_signal_handler_disconnect (gtk_clipboard_get (GDK_SELECTION_PRIMARY), priv->clipboard_change_handler); - priv->clipboard_change_handler = 0; - } - if (priv->queue_change_handler > 0) { - g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), priv->queue_change_handler); - priv->queue_change_handler = 0; - } - if (priv->account_removed_handler > 0) { - g_signal_handler_disconnect (G_OBJECT (modest_runtime_get_account_mgr ()), priv->account_removed_handler); - priv->account_removed_handler = 0; - } + + /* Sanity check: shouldn't be needed, the window mgr should + call this function before */ + modest_msg_view_window_disconnect_signals (MODEST_WINDOW (obj)); + if (priv->header_model != NULL) { g_object_unref (priv->header_model); priv->header_model = NULL; @@ -467,6 +653,17 @@ modest_msg_view_window_finalize (GObject *obj) priv->progress_bar_timeout = 0; } + if (priv->remove_attachment_banner) { + gtk_widget_destroy (priv->remove_attachment_banner); + g_object_unref (priv->remove_attachment_banner); + priv->remove_attachment_banner = NULL; + } + + if (priv->purge_timeout > 0) { + g_source_remove (priv->purge_timeout); + priv->purge_timeout = 0; + } + if (priv->row_reference) { gtk_tree_row_reference_free (priv->row_reference); priv->row_reference = NULL; @@ -491,7 +688,8 @@ select_next_valid_row (GtkTreeModel *model, gboolean cycle) { GtkTreeIter tmp_iter; - GtkTreePath *path, *next; + GtkTreePath *path; + GtkTreePath *next = NULL; gboolean retval = FALSE; g_return_val_if_fail (gtk_tree_row_reference_valid (*row_reference), FALSE); @@ -517,140 +715,69 @@ select_next_valid_row (GtkTreeModel *model, /* Free */ gtk_tree_path_free (path); + if (next) + gtk_tree_path_free (next); return retval; } -ModestWindow * -modest_msg_view_window_new_with_header_model (TnyMsg *msg, - const gchar *modest_account_name, - const gchar *msg_uid, - GtkTreeModel *model, - GtkTreeRowReference *row_reference) -{ - ModestMsgViewWindow *window = NULL; - ModestMsgViewWindowPrivate *priv = NULL; - - window = MODEST_MSG_VIEW_WINDOW(modest_msg_view_window_new (msg, modest_account_name, msg_uid)); - g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL); - - priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); - - g_object_ref (model); - priv->header_model = model; - priv->row_reference = gtk_tree_row_reference_copy (row_reference); - priv->next_row_reference = gtk_tree_row_reference_copy (row_reference); - select_next_valid_row (model, &(priv->next_row_reference), TRUE); - - modest_msg_view_window_update_priority (window); - - /* Check toolbar dimming rules */ - modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); - - return MODEST_WINDOW(window); -} - - -ModestWindow * -modest_msg_view_window_new (TnyMsg *msg, +/* TODO: This should be in _init(), with the parameters as properties. */ +static void +modest_msg_view_window_construct (ModestMsgViewWindow *self, const gchar *modest_account_name, const gchar *msg_uid) { - ModestMsgViewWindow *self = NULL; GObject *obj = NULL; ModestMsgViewWindowPrivate *priv = NULL; ModestWindowPrivate *parent_priv = NULL; ModestDimmingRulesGroup *menu_rules_group = NULL; ModestDimmingRulesGroup *toolbar_rules_group = NULL; - GtkActionGroup *action_group = NULL; - GError *error = NULL; - GdkPixbuf *window_icon; + ModestDimmingRulesGroup *clipboard_rules_group = NULL; - g_return_val_if_fail (msg, NULL); - - obj = g_object_new(MODEST_TYPE_MSG_VIEW_WINDOW, NULL); + obj = G_OBJECT (self); priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(obj); parent_priv = MODEST_WINDOW_GET_PRIVATE(obj); - self = MODEST_MSG_VIEW_WINDOW (obj); priv->msg_uid = g_strdup (msg_uid); - parent_priv->ui_manager = gtk_ui_manager_new(); + /* Menubar */ + parent_priv->menubar = modest_maemo_utils_get_manager_menubar_as_menu (parent_priv->ui_manager, "/MenuBar"); + hildon_window_set_menu (HILDON_WINDOW(obj), GTK_MENU(parent_priv->menubar)); + gtk_widget_show (parent_priv->menubar); parent_priv->ui_dimming_manager = modest_ui_dimming_manager_new(); - action_group = gtk_action_group_new ("ModestMsgViewWindowActions"); - gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE); - - menu_rules_group = modest_dimming_rules_group_new ("ModestMenuDimmingRules", FALSE); - toolbar_rules_group = modest_dimming_rules_group_new ("ModestToolbarDimmingRules", TRUE); - - /* Add common actions */ - gtk_action_group_add_actions (action_group, - modest_action_entries, - G_N_ELEMENTS (modest_action_entries), - obj); - gtk_action_group_add_toggle_actions (action_group, - modest_toggle_action_entries, - G_N_ELEMENTS (modest_toggle_action_entries), - obj); - gtk_action_group_add_toggle_actions (action_group, - msg_view_toggle_action_entries, - G_N_ELEMENTS (msg_view_toggle_action_entries), - obj); - gtk_action_group_add_radio_actions (action_group, - msg_view_zoom_action_entries, - G_N_ELEMENTS (msg_view_zoom_action_entries), - 100, - G_CALLBACK (modest_ui_actions_on_change_zoom), - obj); - - gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0); - g_object_unref (action_group); - - /* Load the UI definition */ - gtk_ui_manager_add_ui_from_file (parent_priv->ui_manager, MODEST_UIDIR "modest-msg-view-window-ui.xml", - &error); - if (error) { - g_printerr ("modest: could not merge modest-msg-view-window-ui.xml: %s\n", error->message); - g_error_free (error); - error = NULL; - } - /* ****** */ + menu_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_MENU, FALSE); + toolbar_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_TOOLBAR, TRUE); + clipboard_rules_group = modest_dimming_rules_group_new (MODEST_DIMMING_RULES_CLIPBOARD, FALSE); /* Add common dimming rules */ modest_dimming_rules_group_add_rules (menu_rules_group, modest_msg_view_menu_dimming_entries, G_N_ELEMENTS (modest_msg_view_menu_dimming_entries), - self); + MODEST_WINDOW (self)); modest_dimming_rules_group_add_rules (toolbar_rules_group, modest_msg_view_toolbar_dimming_entries, G_N_ELEMENTS (modest_msg_view_toolbar_dimming_entries), - self); + MODEST_WINDOW (self)); + modest_dimming_rules_group_add_rules (clipboard_rules_group, + modest_msg_view_clipboard_dimming_entries, + G_N_ELEMENTS (modest_msg_view_clipboard_dimming_entries), + MODEST_WINDOW (self)); /* Insert dimming rules group for this window */ modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, menu_rules_group); modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, toolbar_rules_group); + modest_ui_dimming_manager_insert_rules_group (parent_priv->ui_dimming_manager, clipboard_rules_group); g_object_unref (menu_rules_group); g_object_unref (toolbar_rules_group); + g_object_unref (clipboard_rules_group); - /* Add accelerators */ - gtk_window_add_accel_group (GTK_WINDOW (obj), - gtk_ui_manager_get_accel_group (parent_priv->ui_manager)); - - /* Init window */ - init_window (MODEST_MSG_VIEW_WINDOW(obj), msg); restore_settings (MODEST_MSG_VIEW_WINDOW(obj)); - /* Set window icon */ - window_icon = modest_platform_get_icon (MODEST_APP_MSG_VIEW_ICON); - if (window_icon) { - gtk_window_set_icon (GTK_WINDOW (obj), window_icon); - g_object_unref (window_icon); - } - /* g_signal_connect (G_OBJECT(obj), "delete-event", G_CALLBACK(on_delete_event), obj); */ - g_signal_connect (G_OBJECT(priv->msg_view), "link_clicked", + 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); + g_signal_connect (G_OBJECT(priv->msg_view), "activate_link", G_CALLBACK (modest_ui_actions_on_msg_link_clicked), obj); g_signal_connect (G_OBJECT(priv->msg_view), "link_hover", G_CALLBACK (modest_ui_actions_on_msg_link_hover), obj); @@ -662,13 +789,20 @@ modest_msg_view_window_new (TnyMsg *msg, G_CALLBACK (modest_ui_actions_on_msg_link_contextual), obj); g_signal_connect (G_OBJECT (obj), "key-release-event", - G_CALLBACK (modest_msg_view_window_key_release_event), + G_CALLBACK (modest_msg_view_window_key_event), + NULL); + + g_signal_connect (G_OBJECT (obj), "key-press-event", + G_CALLBACK (modest_msg_view_window_key_event), NULL); g_signal_connect (G_OBJECT (obj), "window-state-event", G_CALLBACK (modest_msg_view_window_window_state_event), NULL); + g_signal_connect (G_OBJECT (obj), "move-focus", + G_CALLBACK (on_move_focus), obj); + /* Mail Operation Queue */ priv->queue_change_handler = g_signal_connect (G_OBJECT (modest_runtime_get_mail_operation_queue ()), "queue-changed", @@ -676,26 +810,355 @@ modest_msg_view_window_new (TnyMsg *msg, obj); /* Account manager */ - priv->account_removed_handler = g_signal_connect (G_OBJECT(modest_runtime_get_account_mgr()), - "account-removed", + priv->account_removed_handler = g_signal_connect (G_OBJECT (modest_runtime_get_account_store ()), + "account_removed", G_CALLBACK(on_account_removed), obj); modest_window_set_active_account (MODEST_WINDOW(obj), modest_account_name); + g_signal_connect (G_OBJECT (priv->find_toolbar), "close", G_CALLBACK (modest_msg_view_window_find_toolbar_close), obj); + g_signal_connect (G_OBJECT (priv->find_toolbar), "search", G_CALLBACK (modest_msg_view_window_find_toolbar_search), obj); priv->last_search = NULL; /* Init the clipboard actions dim status */ modest_msg_view_grab_focus(MODEST_MSG_VIEW (priv->msg_view)); + gtk_widget_show_all (GTK_WIDGET (obj)); + update_window_title (MODEST_MSG_VIEW_WINDOW (obj)); + + +} + +/* FIXME: parameter checks */ +ModestWindow * +modest_msg_view_window_new_with_header_model (TnyMsg *msg, + const gchar *modest_account_name, + const gchar *msg_uid, + GtkTreeModel *model, + GtkTreeRowReference *row_reference) +{ + ModestMsgViewWindow *window = NULL; + ModestMsgViewWindowPrivate *priv = NULL; + TnyFolder *header_folder = NULL; + ModestHeaderView *header_view = NULL; + ModestWindow *main_window = NULL; + ModestWindowMgr *mgr = NULL; + + mgr = modest_runtime_get_window_mgr (); + window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr)); + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL); + + modest_msg_view_window_construct (window, modest_account_name, msg_uid); + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + /* Remember the message list's TreeModel so we can detect changes + * and change the list selection when necessary: */ + + main_window = modest_window_mgr_get_main_window(mgr, FALSE); /* don't create */ + if (main_window) { + header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget( + MODEST_MAIN_WINDOW(main_window), + MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW)); + } + + if (header_view != NULL){ + header_folder = modest_header_view_get_folder(header_view); + priv->is_outbox = (modest_tny_folder_guess_folder_type (header_folder) == TNY_FOLDER_TYPE_OUTBOX); + g_assert(header_folder != NULL); + priv->header_folder_id = tny_folder_get_id(header_folder); + g_assert(priv->header_folder_id != NULL); + g_object_unref(header_folder); + } + + priv->header_model = g_object_ref(model); + if (row_reference) { + priv->row_reference = gtk_tree_row_reference_copy (row_reference); + priv->next_row_reference = gtk_tree_row_reference_copy (row_reference); + select_next_valid_row (model, &(priv->next_row_reference), TRUE); + } else { + priv->row_reference = NULL; + priv->next_row_reference = NULL; + } + + priv->row_changed_handler = g_signal_connect( + GTK_TREE_MODEL(model), "row-changed", + G_CALLBACK(modest_msg_view_window_on_row_changed), + window); + priv->row_deleted_handler = g_signal_connect( + GTK_TREE_MODEL(model), "row-deleted", + G_CALLBACK(modest_msg_view_window_on_row_deleted), + window); + priv->row_inserted_handler = g_signal_connect ( + GTK_TREE_MODEL(model), "row-inserted", + G_CALLBACK(modest_msg_view_window_on_row_inserted), + window); + priv->rows_reordered_handler = g_signal_connect( + GTK_TREE_MODEL(model), "rows-reordered", + G_CALLBACK(modest_msg_view_window_on_row_reordered), + window); + + if (header_view != NULL){ + modest_header_view_add_observer(header_view, + MODEST_HEADER_VIEW_OBSERVER(window)); + } + + tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg); + update_window_title (MODEST_MSG_VIEW_WINDOW (window)); + gtk_widget_show_all (GTK_WIDGET (window)); + modest_msg_view_window_update_priority (window); + + /* Check dimming rules */ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window)); + modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD); + + return MODEST_WINDOW(window); +} + +ModestWindow * +modest_msg_view_window_new_for_search_result (TnyMsg *msg, + const gchar *modest_account_name, + const gchar *msg_uid) +{ + ModestMsgViewWindow *window = NULL; + ModestMsgViewWindowPrivate *priv = NULL; + ModestWindowMgr *mgr = NULL; + + mgr = modest_runtime_get_window_mgr (); + window = MODEST_MSG_VIEW_WINDOW (modest_window_mgr_get_msg_view_window (mgr)); + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), NULL); + modest_msg_view_window_construct (window, modest_account_name, msg_uid); + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + /* Remember that this is a search result, + * so we can disable some UI appropriately: */ + priv->is_search_result = TRUE; + + tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg); + + update_window_title (window); + modest_msg_view_window_update_priority (window); + + + return MODEST_WINDOW(window); +} + +ModestWindow * +modest_msg_view_window_new_for_attachment (TnyMsg *msg, + const gchar *modest_account_name, + const gchar *msg_uid) +{ + GObject *obj = NULL; + ModestMsgViewWindowPrivate *priv; + ModestWindowMgr *mgr = NULL; + + g_return_val_if_fail (msg, NULL); + mgr = modest_runtime_get_window_mgr (); + obj = G_OBJECT (modest_window_mgr_get_msg_view_window (mgr)); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj); + modest_msg_view_window_construct (MODEST_MSG_VIEW_WINDOW (obj), + modest_account_name, msg_uid); + + tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg); update_window_title (MODEST_MSG_VIEW_WINDOW (obj)); - /* Check toolbar dimming rules */ + /* Check dimming rules */ modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (obj)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (obj)); + modest_window_check_dimming_rules_group (MODEST_WINDOW (obj), MODEST_DIMMING_RULES_CLIPBOARD); return MODEST_WINDOW(obj); } +void modest_msg_view_window_on_row_changed( + GtkTreeModel *header_model, + GtkTreePath *arg1, + GtkTreeIter *arg2, + ModestMsgViewWindow *window){ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); +} + +void modest_msg_view_window_on_row_deleted( + GtkTreeModel *header_model, + GtkTreePath *arg1, + ModestMsgViewWindow *window){ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); +} + +/* On insertions we check if the folder still has the message we are + * showing or do not. If do not, we do nothing. Which means we are still + * not attached to any header folder and thus next/prev buttons are + * still dimmed. Once the message that is shown by msg-view is found, the + * new model of header-view will be attached and the references will be set. + * On each further insertions dimming rules will be checked. However + * this requires extra CPU time at least works. + * (An message might be deleted from TnyFolder and thus will not be + * inserted into the model again for example if it is removed by the + * imap server and the header view is refreshed.) + */ +void modest_msg_view_window_on_row_inserted( + GtkTreeModel *new_model, + GtkTreePath *tree_path, + GtkTreeIter *tree_iter, + ModestMsgViewWindow *window){ + ModestMsgViewWindowPrivate *priv = NULL; + TnyHeader *header = NULL; + gchar *uid = NULL; + + g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window)); + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + /* If we already has a model attached then the message shown by + * msg-view is in it, and thus we do not need any actions but + * to check the dimming rules.*/ + if(priv->header_model != NULL){ + if (priv->row_reference) { + gtk_tree_row_reference_free(priv->next_row_reference); + priv->next_row_reference = gtk_tree_row_reference_copy( + priv->row_reference); + select_next_valid_row (priv->header_model, + &(priv->next_row_reference), FALSE); + } + modest_ui_actions_check_toolbar_dimming_rules ( + MODEST_WINDOW (window)); + return; + } + + /* Check if the newly inserted message is the same we are actually + * showing. IF not, we should remain detached from the header model + * and thus prev and next toolbarbuttons should remain dimmed. */ + gtk_tree_model_get (new_model, tree_iter, + TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1); + uid = modest_tny_folder_get_header_unique_id(header); + if(!g_str_equal(priv->msg_uid, uid)){ + g_free(uid); + g_object_unref(G_OBJECT(header)); + header = NULL; + return; + } + if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN)) + tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN); + g_object_unref(G_OBJECT(header)); + header = NULL; + g_free(uid); + + /* Setup row_reference for the actual msg. */ + priv->row_reference = gtk_tree_row_reference_new( + new_model, tree_path); + if(priv->row_reference == NULL){ + g_warning("No reference for msg header item."); + return; + } + + /* Attach new_model and connect some callback to it to become able + * to detect changes in header-view. */ + priv->header_model = g_object_ref(new_model); + g_signal_connect (new_model, "row-changed", + G_CALLBACK (modest_msg_view_window_on_row_changed), + window); + g_signal_connect (new_model, "row-deleted", + G_CALLBACK (modest_msg_view_window_on_row_deleted), + window); + g_signal_connect (new_model, "rows-reordered", + G_CALLBACK (modest_msg_view_window_on_row_reordered), + window); + + /* Now set up next_row_reference. */ + priv->next_row_reference = gtk_tree_row_reference_copy( + priv->row_reference); + select_next_valid_row (priv->header_model, + &(priv->next_row_reference), FALSE); + + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); +} + +void modest_msg_view_window_on_row_reordered( + GtkTreeModel *header_model, + GtkTreePath *arg1, + GtkTreeIter *arg2, + gpointer arg3, + ModestMsgViewWindow *window){ + modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); +} + +/* The modest_msg_view_window_update_model_replaced implements update + * function for ModestHeaderViewObserver. Checks whether the TnyFolder + * actually belongs to the header-view is the same as the TnyFolder of + * the message of msg-view or not. If they are different, there is + * nothing to do. If they are the same, then the model has replaced and + * the reference in msg-view shall be replaced from the old model to + * the new model. In this case the view will be detached from it's + * header folder. From this point the next/prev buttons are dimmed. + */ +void modest_msg_view_window_update_model_replaced( + ModestHeaderViewObserver *observer, + GtkTreeModel *model, + const gchar *tny_folder_id){ + ModestMsgViewWindowPrivate *priv = NULL; + ModestMsgViewWindow *window = NULL; + + g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer)); + g_assert(MODEST_IS_MSG_VIEW_WINDOW(observer)); + + window = MODEST_MSG_VIEW_WINDOW(observer); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(window); + + /* If there is an other folder in the header-view then we do + * not care about it's model (msg list). Else if the + * header-view shows the folder the msg shown by us is in, we + * shall replace our model reference and make some check. */ + if(tny_folder_id == NULL || !g_str_equal(tny_folder_id, priv->header_folder_id)) + return; + + /* Model is changed(replaced), so we should forget the old + * one. Because there might be other references and there + * might be some change on the model even if we unreferenced + * it, we need to disconnect our signals here. */ + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->row_changed_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->row_changed_handler); + priv->row_changed_handler = 0; + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->row_deleted_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->row_deleted_handler); + priv->row_deleted_handler = 0; + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->row_inserted_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->row_inserted_handler); + priv->row_inserted_handler = 0; + if (g_signal_handler_is_connected(G_OBJECT (priv->header_model), + priv->rows_reordered_handler)) + g_signal_handler_disconnect(G_OBJECT (priv->header_model), + priv->rows_reordered_handler); + priv->rows_reordered_handler = 0; + g_object_unref(priv->header_model); + priv->header_model = NULL; + if (priv->row_reference) + g_object_unref(priv->row_reference); + priv->row_reference = NULL; + if (priv->next_row_reference) + g_object_unref(priv->next_row_reference); + priv->next_row_reference = NULL; + + modest_ui_actions_check_toolbar_dimming_rules(MODEST_WINDOW(window)); + + g_assert(model != NULL); + + /* Also we must connect to the new model for row insertions. + * Only for insertions now. We will need other ones only after + * the msg is show by msg-view is added to the new model. */ + priv->row_inserted_handler = g_signal_connect ( + model, "row-inserted", + G_CALLBACK(modest_msg_view_window_on_row_inserted), + window); +} gboolean modest_msg_view_window_toolbar_on_transfer_mode (ModestMsgViewWindow *self) @@ -720,15 +1183,25 @@ modest_msg_view_window_get_header (ModestMsgViewWindow *self) g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), NULL); priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); - /* Message is not obtained from a treemodel (Attachment ?) */ - if (priv->header_model == NULL) { + /* If the message was not obtained from a treemodel, + * for instance if it was opened directly by the search UI: + */ + if (priv->header_model == NULL || priv->row_reference == NULL) { msg = modest_msg_view_window_get_message (self); - header = tny_msg_get_header (msg); - g_object_unref (msg); + if (msg) { + header = tny_msg_get_header (msg); + g_object_unref (msg); + } return header; } - /* Get current message iter */ + /* Get iter of the currently selected message in the header view: */ + /* TODO: Why not just give this window a ref of the TnyHeader or TnyMessage, + * instead of sometimes retrieving it from the header view? + * Then we wouldn't be dependent on the message actually still being selected + * in the header view. murrayc. */ + if (!gtk_tree_row_reference_valid (priv->row_reference)) + return NULL; path = gtk_tree_row_reference_get_path (priv->row_reference); g_return_val_if_fail (path != NULL, NULL); gtk_tree_model_get_iter (priv->header_model, @@ -747,16 +1220,13 @@ modest_msg_view_window_get_header (ModestMsgViewWindow *self) TnyMsg* modest_msg_view_window_get_message (ModestMsgViewWindow *self) { - ModestMsgView *msg_view; ModestMsgViewWindowPrivate *priv; - + g_return_val_if_fail (self, NULL); - + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self); - - msg_view = MODEST_MSG_VIEW (priv->msg_view); - - return modest_msg_view_get_message (msg_view); + + return tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)); } const gchar* @@ -792,9 +1262,9 @@ modest_msg_view_window_toggle_find_toolbar (GtkToggleAction *toggle, /* update the toggle buttons status */ action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/FindInMessage"); - modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active); action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsFindInMessageMenu"); - modest_maemo_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), is_active); } @@ -817,7 +1287,7 @@ modest_msg_view_window_find_toolbar_search (GtkWidget *widget, gchar *current_search; ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (obj); - if (modest_msg_view_get_message_is_empty (MODEST_MSG_VIEW (priv->msg_view))) { + if (modest_mime_part_view_is_empty (MODEST_MIME_PART_VIEW (priv->msg_view))) { hildon_banner_show_information (NULL, NULL, _("mail_ib_nothing_to_find")); return; } @@ -834,18 +1304,22 @@ modest_msg_view_window_find_toolbar_search (GtkWidget *widget, gboolean result; g_free (priv->last_search); priv->last_search = g_strdup (current_search); - result = modest_msg_view_search (MODEST_MSG_VIEW (priv->msg_view), - priv->last_search); + result = modest_isearch_view_search (MODEST_ISEARCH_VIEW (priv->msg_view), + priv->last_search); if (!result) { hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_no_matches")); g_free (priv->last_search); priv->last_search = NULL; - } + } else { + modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view)); + } } else { - if (!modest_msg_view_search_next (MODEST_MSG_VIEW (priv->msg_view))) { + if (!modest_isearch_view_search_next (MODEST_ISEARCH_VIEW (priv->msg_view))) { hildon_banner_show_information (NULL, NULL, dgettext("hildon-libs", "ckct_ib_find_search_complete")); g_free (priv->last_search); priv->last_search = NULL; + } else { + modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view)); } } @@ -858,11 +1332,20 @@ modest_msg_view_window_set_zoom (ModestWindow *window, gdouble zoom) { ModestMsgViewWindowPrivate *priv; + ModestWindowPrivate *parent_priv; + GtkAction *action = NULL; + gint int_zoom = (gint) rint (zoom*100.0+0.1); g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window)); priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); - modest_msg_view_set_zoom (MODEST_MSG_VIEW (priv->msg_view), zoom); + parent_priv = MODEST_WINDOW_GET_PRIVATE (window); + modest_zoomable_set_zoom (MODEST_ZOOMABLE (priv->msg_view), zoom); + + action = gtk_ui_manager_get_action (parent_priv->ui_manager, + "/MenuBar/ViewMenu/ZoomMenu/Zoom50Menu"); + + gtk_radio_action_set_current_value (GTK_RADIO_ACTION (action), int_zoom); } static gdouble @@ -873,7 +1356,7 @@ modest_msg_view_window_get_zoom (ModestWindow *window) g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), 1.0); priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); - return modest_msg_view_get_zoom (MODEST_MSG_VIEW (priv->msg_view)); + return modest_zoomable_get_zoom (MODEST_ZOOMABLE (priv->msg_view)); } static gboolean @@ -932,92 +1415,136 @@ modest_msg_view_window_zoom_minus (ModestWindow *window) } static gboolean -modest_msg_view_window_key_release_event (GtkWidget *window, - GdkEventKey *event, - gpointer userdata) +modest_msg_view_window_key_event (GtkWidget *window, + GdkEventKey *event, + gpointer userdata) { - if (event->type == GDK_KEY_RELEASE) { - switch (event->keyval) { - case GDK_Up: - modest_msg_view_window_scroll_up (MODEST_WINDOW (window)); + GtkWidget *focus; + + focus = gtk_window_get_focus (GTK_WINDOW (window)); + + /* for the find toolbar case */ + if (focus && GTK_IS_ENTRY (focus)) { + if (event->keyval == GDK_BackSpace) { + GdkEvent *copy; + copy = gdk_event_copy ((GdkEvent *) event); + gtk_widget_event (focus, copy); + gdk_event_free (copy); return TRUE; - break; - case GDK_Down: - modest_msg_view_window_scroll_down (MODEST_WINDOW (window)); + } else + return FALSE; + } + if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up || + event->keyval == GDK_Down || event->keyval == GDK_KP_Down || + event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up || + event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down || + event->keyval == GDK_Home || event->keyval == GDK_KP_Home || + event->keyval == GDK_End || event->keyval == GDK_KP_End) { + ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + gboolean return_value; + + if (event->type == GDK_KEY_RELEASE) { + GtkScrollType scroll_type; + + switch (event->keyval) { + case GDK_Up: + case GDK_KP_Up: + scroll_type = GTK_SCROLL_STEP_UP; break; + case GDK_Down: + case GDK_KP_Down: + scroll_type = GTK_SCROLL_STEP_DOWN; break; + case GDK_Page_Up: + case GDK_KP_Page_Up: + scroll_type = GTK_SCROLL_PAGE_UP; break; + case GDK_Page_Down: + case GDK_KP_Page_Down: + scroll_type = GTK_SCROLL_PAGE_DOWN; break; + case GDK_Home: + case GDK_KP_Home: + scroll_type = GTK_SCROLL_START; break; + case GDK_End: + case GDK_KP_End: + scroll_type = GTK_SCROLL_END; break; + default: scroll_type = GTK_SCROLL_NONE; + } + + g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", + scroll_type, FALSE, &return_value); return TRUE; - break; - default: + } else { return FALSE; - break; - }; + } } else { return FALSE; } } -static void -modest_msg_view_window_scroll_up (ModestWindow *window) +gboolean +modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window) { + GtkTreePath *path; ModestMsgViewWindowPrivate *priv; - gboolean return_value; + GtkTreeIter tmp_iter; + gboolean is_last_selected; + g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE); priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); - g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_UP, FALSE, &return_value); -} -static void -modest_msg_view_window_scroll_down (ModestWindow *window) -{ - ModestMsgViewWindowPrivate *priv; - gboolean return_value; + /*if no model (so no rows at all), then virtually we are the last*/ + if (!priv->header_model || !priv->row_reference) + return TRUE; - priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); - g_signal_emit_by_name (G_OBJECT (priv->main_scroll), "scroll-child", GTK_SCROLL_STEP_DOWN, FALSE, &return_value); + path = gtk_tree_row_reference_get_path (priv->row_reference); + if (path == NULL) + return TRUE; + + is_last_selected = TRUE; + while (is_last_selected) { + TnyHeader *header; + gtk_tree_path_next (path); + if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path)) + break; + gtk_tree_model_get (priv->header_model, &tmp_iter, + TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, + &header, -1); + if (header) { + if (msg_is_visible (header, priv->is_outbox)) + is_last_selected = FALSE; + g_object_unref(G_OBJECT(header)); + } + } + gtk_tree_path_free (path); + return is_last_selected; } gboolean -modest_msg_view_window_last_message_selected (ModestMsgViewWindow *window) +modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window) { - GtkTreePath *path; ModestMsgViewWindowPrivate *priv; - GtkTreeIter tmp_iter; - gboolean has_next = FALSE; g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE); priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); - if (priv->header_model) { - path = gtk_tree_row_reference_get_path (priv->row_reference); - if (path == NULL) return FALSE; - while (!has_next) { - TnyHeader *header; - gtk_tree_path_next (path); - if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path)) - break; - gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, - &header, -1); - if (!(tny_header_get_flags(header)&TNY_HEADER_FLAG_DELETED)) { - has_next = TRUE; - break; - } - } - gtk_tree_path_free (path); - return !has_next; - } else { - return TRUE; - } - + return priv->header_model != NULL; } gboolean -modest_msg_view_window_has_headers_model (ModestMsgViewWindow *window) +modest_msg_view_window_is_search_result (ModestMsgViewWindow *window) { ModestMsgViewWindowPrivate *priv; g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE); priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); - return priv->header_model != NULL; + return priv->is_search_result; +} + +static gboolean +msg_is_visible (TnyHeader *header, gboolean check_outbox) +{ + return (!(tny_header_get_flags(header) & TNY_HEADER_FLAG_DELETED)) && + ( (!check_outbox) || (modest_tny_all_send_queues_get_msg_status (header) != MODEST_TNY_SEND_QUEUE_FAILED)) ; + } gboolean @@ -1025,53 +1552,49 @@ modest_msg_view_window_first_message_selected (ModestMsgViewWindow *window) { GtkTreePath *path; ModestMsgViewWindowPrivate *priv; - gboolean result; + gboolean is_first_selected; GtkTreeIter tmp_iter; +/* gchar * path_string;*/ g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), TRUE); priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); - if (priv->header_model) { - gchar * path_string; - path = gtk_tree_row_reference_get_path (priv->row_reference); - if (!path) - return TRUE; + /*if no model (so no rows at all), then virtually we are the first*/ + if (!priv->header_model || !priv->row_reference) + return TRUE; - path_string = gtk_tree_path_to_string (path); - result = (strcmp (path_string, "0")==0); - if (result) { - g_free (path_string); - gtk_tree_path_free (path); - return result; - } + path = gtk_tree_row_reference_get_path (priv->row_reference); + if (!path) + return TRUE; - while (result) { - TnyHeader *header; +/* path_string = gtk_tree_path_to_string (path); + is_first_selected = strcmp (path_string, "0"); - gtk_tree_path_prev (path); - - if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path)) - break; - gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, - &header, -1); - if (!(tny_header_get_flags(header)&TNY_HEADER_FLAG_DELETED)) { - result = FALSE; - break; - } + g_free (path_string); + gtk_tree_path_free (path); - path_string = gtk_tree_path_to_string (path); - if (strcmp(path_string, "0")==0) { - g_free (path_string); - break; - } - g_free (path_string); + return is_first_selected;*/ + + is_first_selected = TRUE; + while (is_first_selected) { + TnyHeader *header; + if(!gtk_tree_path_prev (path)) + break; + /* Here the 'if' is needless for logic, but let make sure + * iter is valid for gtk_tree_model_get. */ + if (!gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path)) + break; + gtk_tree_model_get (priv->header_model, &tmp_iter, + TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, + &header, -1); + if (header) { + if (msg_is_visible (header, priv->is_outbox)) + is_first_selected = FALSE; + g_object_unref(G_OBJECT(header)); } - gtk_tree_path_free (path); - return result; - } else { - return TRUE; } - + gtk_tree_path_free (path); + return is_first_selected; } /** @@ -1090,54 +1613,63 @@ static gboolean message_reader (ModestMsgViewWindow *window, ModestMsgViewWindowPrivate *priv, TnyHeader *header, - GtkTreePath *path) + GtkTreeRowReference *row_reference) { ModestMailOperation *mail_op = NULL; - ModestMailOperationTypeOperation op_type; - - g_return_val_if_fail (path != NULL, FALSE); + gboolean already_showing = FALSE; + ModestWindow *msg_window = NULL; + ModestWindowMgr *mgr; + + g_return_val_if_fail (row_reference != NULL, FALSE); + + mgr = modest_runtime_get_window_mgr (); + already_showing = modest_window_mgr_find_registered_header (mgr, header, &msg_window); + if (already_showing && (msg_window != MODEST_WINDOW (window))) { + gboolean retval; + if (msg_window) + gtk_window_present (GTK_WINDOW (msg_window)); + g_signal_emit_by_name (G_OBJECT (window), "delete-event", NULL, &retval); + return TRUE; + } /* Msg download completed */ - if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED) { - op_type = MODEST_MAIL_OPERATION_TYPE_OPEN; - } else { - TnyFolder *folder; - GtkResponseType response; - - op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE; - - /* Ask the user if he wants to download the message */ - response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window), - _("mcen_nc_get_msg")); - if (response == GTK_RESPONSE_CANCEL) - return FALSE; + if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)) { + /* Ask the user if he wants to download the message if + we're not online */ + if (!tny_device_is_online (modest_runtime_get_device())) { + TnyFolder *folder = NULL; + GtkResponseType response; + + response = modest_platform_run_confirmation_dialog (GTK_WINDOW (window), + _("mcen_nc_get_msg")); + if (response == GTK_RESPONSE_CANCEL) + return FALSE; - /* Offer the connection dialog if necessary */ - /* FIXME: should this stuff go directly to the mail - operation instead of spread it all over the - code? */ - folder = tny_header_get_folder (header); - if (folder) { - if (!modest_platform_connect_and_wait_if_network_folderstore (NULL, TNY_FOLDER_STORE (folder))) { + /* Offer the connection dialog if necessary */ + folder = tny_header_get_folder (header); + if (folder) { + if (!modest_platform_connect_and_wait_if_network_folderstore (NULL, + TNY_FOLDER_STORE (folder))) { + g_object_unref (folder); + return FALSE; + } g_object_unref (folder); - return FALSE; } - g_object_unref (folder); } } /* New mail operation */ - mail_op = modest_mail_operation_new_with_error_handling (op_type, - G_OBJECT(window), + mail_op = modest_mail_operation_new_with_error_handling (G_OBJECT(window), modest_ui_actions_get_msgs_full_error_handler, - NULL); + NULL, NULL); modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_op); - modest_mail_operation_get_msg (mail_op, header, view_msg_cb, path); + modest_mail_operation_get_msg (mail_op, header, view_msg_cb, row_reference); g_object_unref (mail_op); - /* Update toolbar dimming rules */ + /* Update dimming rules */ modest_ui_actions_check_toolbar_dimming_rules (MODEST_WINDOW (window)); + modest_ui_actions_check_menu_dimming_rules (MODEST_WINDOW (window)); return TRUE; } @@ -1150,26 +1682,46 @@ modest_msg_view_window_select_next_message (ModestMsgViewWindow *window) GtkTreeIter tmp_iter; TnyHeader *header; gboolean retval = TRUE; + GtkTreeRowReference *row_reference = NULL; g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE); priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); - path = gtk_tree_row_reference_get_path (priv->next_row_reference); - if (path == NULL) + if (!priv->row_reference) return FALSE; + /* Update the next row reference if it's not valid. This could + happen if for example the header which it was pointing to, + was deleted. The best place to do it is in the row-deleted + handler but the tinymail model do not work like the glib + tree models and reports the deletion when the row is still + there */ + if (!gtk_tree_row_reference_valid (priv->next_row_reference)) { + if (gtk_tree_row_reference_valid (priv->row_reference)) { + priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference); + select_next_valid_row (priv->header_model, &(priv->next_row_reference), FALSE); + } + } + if (priv->next_row_reference) + path = gtk_tree_row_reference_get_path (priv->next_row_reference); + if (path == NULL) + return FALSE; + + row_reference = gtk_tree_row_reference_copy (priv->next_row_reference); + gtk_tree_model_get_iter (priv->header_model, &tmp_iter, path); + gtk_tree_path_free (path); gtk_tree_model_get (priv->header_model, &tmp_iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1); /* Read the message & show it */ - if (!message_reader (window, priv, header, path)) { + if (!message_reader (window, priv, header, row_reference)) { retval = FALSE; - gtk_tree_path_free (path); + gtk_tree_row_reference_free (row_reference); } /* Free */ @@ -1178,58 +1730,23 @@ modest_msg_view_window_select_next_message (ModestMsgViewWindow *window) return retval; } -gboolean -modest_msg_view_window_select_first_message (ModestMsgViewWindow *self) -{ - ModestMsgViewWindowPrivate *priv = NULL; - TnyHeader *header = NULL; - GtkTreeIter iter; - GtkTreePath *path; - - g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self), FALSE); - priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); - - /* Check that the model is not empty */ - if (!gtk_tree_model_get_iter_first (priv->header_model, &iter)) - return FALSE; - - /* Get the header */ - gtk_tree_model_get (priv->header_model, - &iter, - TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, - &header, -1); - g_return_val_if_fail (TNY_IS_HEADER (header), FALSE); - if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) { - g_object_unref (header); - return modest_msg_view_window_select_next_message (self); - } - - path = gtk_tree_model_get_path (priv->header_model, &iter); - - /* Read the message & show it */ - message_reader (self, priv, header, path); - - /* Free */ - g_object_unref (header); - - return TRUE; -} - gboolean modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window) { ModestMsgViewWindowPrivate *priv = NULL; GtkTreePath *path; + gboolean finished = FALSE; + gboolean retval = FALSE; g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window), FALSE); priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); /* Return inmediatly if there is no header model */ - if (!priv->header_model) + if (!priv->header_model || !priv->row_reference) return FALSE; path = gtk_tree_row_reference_get_path (priv->row_reference); - while (gtk_tree_path_prev (path)) { + while (!finished && gtk_tree_path_prev (path)) { TnyHeader *header; GtkTreeIter iter; @@ -1237,42 +1754,47 @@ modest_msg_view_window_select_previous_message (ModestMsgViewWindow *window) gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1); - if (!header) - break; - if (tny_header_get_flags (header) & TNY_HEADER_FLAG_DELETED) { - g_object_unref (header); - continue; - } - - /* Read the message & show it */ - if (!message_reader (window, priv, header, path)) { + finished = TRUE; + if (header) { + if (msg_is_visible (header, priv->is_outbox)) { + GtkTreeRowReference *row_reference; + row_reference = gtk_tree_row_reference_new (priv->header_model, path); + /* Read the message & show it */ + retval = message_reader (window, priv, header, row_reference); + if (!retval) + gtk_tree_row_reference_free (row_reference); + } else { + finished = FALSE; + } g_object_unref (header); - break; } - - g_object_unref (header); - - return TRUE; } gtk_tree_path_free (path); - return FALSE; + return retval; } static void view_msg_cb (ModestMailOperation *mail_op, TnyHeader *header, + gboolean canceled, TnyMsg *msg, + GError *error, gpointer user_data) { ModestMsgViewWindow *self = NULL; ModestMsgViewWindowPrivate *priv = NULL; - GtkTreePath *path; + GtkTreeRowReference *row_reference = NULL; + if (canceled) { + g_object_unref (self); + return; + } + /* If there was any error */ - path = (GtkTreePath *) user_data; + row_reference = (GtkTreeRowReference *) user_data; if (!modest_ui_actions_msg_retrieval_check (mail_op, header, msg)) { - gtk_tree_path_free (path); + gtk_tree_row_reference_free (row_reference); return; } @@ -1282,21 +1804,28 @@ view_msg_cb (ModestMailOperation *mail_op, priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); /* Update the row reference */ - gtk_tree_row_reference_free (priv->row_reference); - priv->row_reference = gtk_tree_row_reference_new (priv->header_model, path); - priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference); - select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE); - gtk_tree_path_free (path); + if (priv->row_reference != NULL) { + gtk_tree_row_reference_free (priv->row_reference); + priv->row_reference = gtk_tree_row_reference_copy (row_reference); + if (priv->next_row_reference != NULL) { + gtk_tree_row_reference_free (priv->next_row_reference); + } + priv->next_row_reference = gtk_tree_row_reference_copy (priv->row_reference); + select_next_valid_row (priv->header_model, &(priv->next_row_reference), TRUE); + gtk_tree_row_reference_free (row_reference); + } /* Mark header as read */ if (!(tny_header_get_flags (header) & TNY_HEADER_FLAG_SEEN)) - tny_header_set_flags (header, TNY_HEADER_FLAG_SEEN); + tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN); /* Set new message */ - modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg); - modest_msg_view_window_update_priority (self); - update_window_title (MODEST_MSG_VIEW_WINDOW (self)); - modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view)); + if (priv->msg_view != NULL && TNY_IS_MSG_VIEW (priv->msg_view)) { + tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg); + modest_msg_view_window_update_priority (self); + update_window_title (MODEST_MSG_VIEW_WINDOW (self)); + modest_msg_view_grab_focus (MODEST_MSG_VIEW (priv->msg_view)); + } /* Set the new message uid of the window */ if (priv->msg_uid) { @@ -1323,20 +1852,13 @@ modest_msg_view_window_get_folder_type (ModestMsgViewWindow *window) folder_type = TNY_FOLDER_TYPE_UNKNOWN; - msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view)); + msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)); if (msg) { TnyFolder *folder; folder = tny_msg_get_folder (msg); - if (folder) { folder_type = tny_folder_get_folder_type (folder); - - if (folder_type == TNY_FOLDER_TYPE_NORMAL || folder_type == TNY_FOLDER_TYPE_UNKNOWN) { - const gchar *fname = tny_folder_get_name (folder); - folder_type = modest_tny_folder_guess_folder_type_from_name (fname); - } - g_object_unref (folder); } g_object_unref (msg); @@ -1350,12 +1872,12 @@ static void modest_msg_view_window_update_priority (ModestMsgViewWindow *window) { ModestMsgViewWindowPrivate *priv; + TnyHeader *header = NULL; TnyHeaderFlags flags = 0; priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); - if (priv->header_model) { - TnyHeader *header; + if (priv->header_model && priv->row_reference) { GtkTreeIter iter; GtkTreePath *path = NULL; @@ -1367,14 +1889,72 @@ modest_msg_view_window_update_priority (ModestMsgViewWindow *window) gtk_tree_model_get (priv->header_model, &iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header, -1); - flags = tny_header_get_flags (header); gtk_tree_path_free (path); + } else { + TnyMsg *msg; + msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)); + if (msg) { + header = tny_msg_get_header (msg); + g_object_unref (msg); + } + } + + if (header) { + flags = tny_header_get_flags (header); + g_object_unref(G_OBJECT(header)); } modest_msg_view_set_priority (MODEST_MSG_VIEW(priv->msg_view), flags); } +static void +toolbar_resize (ModestMsgViewWindow *self) +{ + ModestMsgViewWindowPrivate *priv = NULL; + ModestWindowPrivate *parent_priv = NULL; + GtkWidget *widget; + gint static_button_size; + ModestWindowMgr *mgr; + + g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self)); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + parent_priv = MODEST_WINDOW_GET_PRIVATE(self); + + mgr = modest_runtime_get_window_mgr (); + static_button_size = modest_window_mgr_get_fullscreen_mode (mgr)?118:108; + + if (parent_priv->toolbar) { + /* left size buttons */ + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); + gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageMoveTo"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); + gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarDeleteMessage"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); + gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/FindInMessage"); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); + gtk_widget_set_size_request (GTK_WIDGET (widget), static_button_size, -1); + + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), FALSE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->next_toolitem), TRUE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->next_toolitem), TRUE); + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->prev_toolitem), TRUE); + } + +} + static gboolean modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer userdata) { @@ -1395,34 +1975,13 @@ modest_msg_view_window_window_state_event (GtkWidget *widget, GdkEventWindowStat if (is_fullscreen != active) { gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), is_fullscreen); } + toolbar_resize (MODEST_MSG_VIEW_WINDOW (widget)); } return FALSE; } -void -modest_msg_view_window_toggle_fullscreen (ModestMsgViewWindow *window) -{ - ModestWindowPrivate *parent_priv; - GtkAction *fs_toggle_action; - parent_priv = MODEST_WINDOW_GET_PRIVATE (window); - - fs_toggle_action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ViewMenu/ViewToggleFullscreenMenu"); - gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (fs_toggle_action), - !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (fs_toggle_action))); -} - -static void -set_homogeneous (GtkWidget *widget, - gpointer data) -{ - if (GTK_IS_TOOL_ITEM (widget)) { - gtk_tool_item_set_expand (GTK_TOOL_ITEM (widget), TRUE); - gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), TRUE); - } -} - static void modest_msg_view_window_show_toolbar (ModestWindow *self, gboolean show_toolbar) @@ -1432,6 +1991,8 @@ modest_msg_view_window_show_toolbar (ModestWindow *self, GtkWidget *reply_button = NULL, *menu = NULL; GtkWidget *placeholder = NULL; gint insert_index; + const gchar *action_name; + GtkAction *action; parent_priv = MODEST_WINDOW_GET_PRIVATE(self); priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self); @@ -1442,22 +2003,16 @@ modest_msg_view_window_show_toolbar (ModestWindow *self, if (!parent_priv->toolbar) { parent_priv->toolbar = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar"); - - /* Set homogeneous toolbar */ - gtk_container_foreach (GTK_CONTAINER (parent_priv->toolbar), - set_homogeneous, NULL); + gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE); priv->progress_toolitem = GTK_WIDGET (gtk_tool_item_new ()); priv->cancel_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarCancel"); priv->next_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageNext"); priv->prev_toolitem = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageBack"); - gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE); - gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->progress_toolitem), TRUE); - gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE); - gtk_tool_item_set_expand (GTK_TOOL_ITEM (priv->cancel_toolitem), FALSE); + toolbar_resize (MODEST_MSG_VIEW_WINDOW (self)); /* Add ProgressBar (Transfer toolbar) */ - priv->progress_bar = modest_progress_bar_widget_new (); + priv->progress_bar = modest_progress_bar_new (); gtk_widget_set_no_show_all (priv->progress_bar, TRUE); placeholder = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ProgressbarView"); insert_index = gtk_toolbar_get_item_index(GTK_TOOLBAR (parent_priv->toolbar), GTK_TOOL_ITEM(placeholder)); @@ -1476,7 +2031,6 @@ modest_msg_view_window_show_toolbar (ModestWindow *self, hildon_window_add_toolbar (HILDON_WINDOW (self), GTK_TOOLBAR (parent_priv->toolbar)); - /* Set reply button tap and hold menu */ reply_button = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarMessageReply"); @@ -1489,14 +2043,31 @@ modest_msg_view_window_show_toolbar (ModestWindow *self, /* Quick hack: this prevents toolbar icons "dance" when progress bar show status is changed */ /* TODO: resize mode migth be GTK_RESIZE_QUEUE, in order to avoid unneccesary shows */ gtk_container_set_resize_mode (GTK_CONTAINER(parent_priv->toolbar), GTK_RESIZE_IMMEDIATE); - + gtk_widget_show (GTK_WIDGET (parent_priv->toolbar)); - set_toolbar_mode (MODEST_MSG_VIEW_WINDOW(self), TOOLBAR_MODE_NORMAL); - + if (modest_msg_view_window_transfer_mode_enabled (MODEST_MSG_VIEW_WINDOW (self))) + set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_TRANSFER); + else + set_toolbar_mode (MODEST_MSG_VIEW_WINDOW (self), TOOLBAR_MODE_NORMAL); + } else { gtk_widget_set_no_show_all (parent_priv->toolbar, TRUE); gtk_widget_hide (GTK_WIDGET (parent_priv->toolbar)); } + + /* Update also the actions (to update the toggles in the + menus), we have to do it manually because some other window + of the same time could have changed it (remember that the + toolbar fullscreen mode is shared by all the windows of the + same type */ + if (modest_window_mgr_get_fullscreen_mode (modest_runtime_get_window_mgr ())) + action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarFullScreenMenu"; + else + action_name = "/MenuBar/ViewMenu/ViewShowToolbarMenu/ViewShowToolbarNormalScreenMenu"; + + action = gtk_ui_manager_get_action (parent_priv->ui_manager, action_name); + modest_utils_toggle_action_set_active_block_notify (GTK_TOGGLE_ACTION (action), + show_toolbar); } static void @@ -1504,34 +2075,10 @@ modest_msg_view_window_clipboard_owner_change (GtkClipboard *clipboard, GdkEvent *event, ModestMsgViewWindow *window) { - ModestWindowPrivate *parent_priv; -/* GtkAction *action; */ - gboolean is_address; - gchar *selection; - GtkWidget *focused; - if (!GTK_WIDGET_VISIBLE (window)) return; - parent_priv = MODEST_WINDOW_GET_PRIVATE (window); - selection = gtk_clipboard_wait_for_text (clipboard); - - is_address = ((selection != NULL) && (modest_text_utils_validate_recipient (selection, NULL))); - -/* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/ToolsMenu/ToolsAddToContactsMenu"); */ -/* gtk_action_set_sensitive (action, is_address); */ - - focused = gtk_window_get_focus (GTK_WINDOW (window)); - -/* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCopyMenu"); */ -/* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */ - -/* action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/EditCutMenu"); */ -/* gtk_action_set_sensitive (action, (selection != NULL) && (!MODEST_IS_ATTACHMENTS_VIEW (focused))); */ - - g_free (selection); -/* modest_msg_view_window_update_dimmed (window); */ - + modest_window_check_dimming_rules_group (MODEST_WINDOW (window), MODEST_DIMMING_RULES_CLIPBOARD); } gboolean @@ -1584,94 +2131,123 @@ observers_empty (ModestMsgViewWindow *self) } static void -on_account_removed (ModestAccountMgr *mgr, - const gchar *account, - gboolean server_account, +on_account_removed (TnyAccountStore *account_store, + TnyAccount *account, gpointer user_data) { - ModestTnyAccountStore *store = modest_runtime_get_account_store (); - const gchar *our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data)); + /* Do nothing if it's a transport account, because we only + show the messages of a store account */ + if (tny_account_get_account_type(account) == TNY_ACCOUNT_TYPE_STORE) { + const gchar *parent_acc = NULL; + const gchar *our_acc = NULL; - TnyAccount *tny_acc = modest_tny_account_store_get_tny_account_by (store, MODEST_TNY_ACCOUNT_STORE_QUERY_ID, account); - if(tny_acc != NULL) - { - const gchar* parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (tny_acc); + our_acc = modest_window_get_active_account (MODEST_WINDOW (user_data)); + parent_acc = modest_tny_account_get_parent_modest_account_name_for_server_account (account); + + /* Close this window if I'm showing a message of the removed account */ if (strcmp (parent_acc, our_acc) == 0) - { - gtk_widget_destroy (GTK_WIDGET (user_data)); - } + modest_ui_actions_on_close_window (NULL, MODEST_WINDOW (user_data)); } } -static void -on_queue_changed (ModestMailOperationQueue *queue, - ModestMailOperation *mail_op, - ModestMailOperationQueueNotification type, - ModestMsgViewWindow *self) +static void +on_mail_operation_started (ModestMailOperation *mail_op, + gpointer user_data) { + ModestMsgViewWindow *self; + ModestMailOperationTypeOperation op_type; GSList *tmp; ModestMsgViewWindowPrivate *priv; - ModestMailOperationTypeOperation op_type; - ModestToolBarModes mode; - - g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (self)); - priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE(self); - - /* If this operations was created by another window, do nothing */ - if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) - return; + GObject *source = NULL; - /* Get toolbar mode from operation id*/ + self = MODEST_MSG_VIEW_WINDOW (user_data); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); op_type = modest_mail_operation_get_type_operation (mail_op); - switch (op_type) { -/* case MODEST_MAIL_OPERATION_TYPE_SEND: */ - case MODEST_MAIL_OPERATION_TYPE_RECEIVE: - case MODEST_MAIL_OPERATION_TYPE_OPEN: - mode = TOOLBAR_MODE_TRANSFER; - break; - default: - mode = TOOLBAR_MODE_NORMAL; - - } - - /* Add operation observers and change toolbar if neccessary*/ tmp = priv->progress_widgets; - switch (type) { - case MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED: - if (mode == TOOLBAR_MODE_TRANSFER) { - /* Enable transfer toolbar mode */ + source = modest_mail_operation_get_source(mail_op); + if (G_OBJECT (self) == source) { + if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) { set_toolbar_transfer_mode(self); while (tmp) { - modest_progress_object_add_operation (MODEST_PROGRESS_OBJECT (tmp->data), - mail_op); + modest_progress_object_add_operation ( + MODEST_PROGRESS_OBJECT (tmp->data), + mail_op); tmp = g_slist_next (tmp); } - } - break; - case MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED: - if (mode == TOOLBAR_MODE_TRANSFER) { - while (tmp) { - modest_progress_object_remove_operation (MODEST_PROGRESS_OBJECT (tmp->data), - mail_op); - tmp = g_slist_next (tmp); - - } + } + g_object_unref (source); +} - /* If no more operations are being observed, NORMAL mode is enabled again */ - if (observers_empty (self)) { - set_toolbar_mode (self, TOOLBAR_MODE_NORMAL); - } +static void +on_mail_operation_finished (ModestMailOperation *mail_op, + gpointer user_data) +{ + ModestMsgViewWindow *self; + ModestMailOperationTypeOperation op_type; + GSList *tmp; + ModestMsgViewWindowPrivate *priv; + + self = MODEST_MSG_VIEW_WINDOW (user_data); + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + op_type = modest_mail_operation_get_type_operation (mail_op); + tmp = priv->progress_widgets; + + if (op_type == MODEST_MAIL_OPERATION_TYPE_RECEIVE || op_type == MODEST_MAIL_OPERATION_TYPE_OPEN ) { + while (tmp) { + modest_progress_object_remove_operation ( + MODEST_PROGRESS_OBJECT (tmp->data), + mail_op); + tmp = g_slist_next (tmp); } - break; + + /* If no more operations are being observed, NORMAL mode is enabled again */ + if (observers_empty (self)) { + set_toolbar_mode (self, TOOLBAR_MODE_NORMAL); + } + } +} + +static void +on_queue_changed (ModestMailOperationQueue *queue, + ModestMailOperation *mail_op, + ModestMailOperationQueueNotification type, + ModestMsgViewWindow *self) +{ + ModestMsgViewWindowPrivate *priv; + + priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (self); + + /* If this operations was created by another window, do nothing */ + if (!modest_mail_operation_is_mine (mail_op, G_OBJECT(self))) + return; + + if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_ADDED) { + priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (mail_op), + "operation-started", + G_CALLBACK (on_mail_operation_started), + self); + priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers, + G_OBJECT (mail_op), + "operation-finished", + G_CALLBACK (on_mail_operation_finished), + self); + } else if (type == MODEST_MAIL_OPERATION_QUEUE_OPERATION_REMOVED) { + priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, + G_OBJECT (mail_op), + "operation-started"); + priv->sighandlers = modest_signal_mgr_disconnect (priv->sighandlers, + G_OBJECT (mail_op), + "operation-finished"); } } -GList * +TnyList * modest_msg_view_window_get_attachments (ModestMsgViewWindow *win) { ModestMsgViewWindowPrivate *priv; - GList *selected_attachments = NULL; + TnyList *selected_attachments = NULL; g_return_val_if_fail (MODEST_IS_MSG_VIEW_WINDOW (win), NULL); priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (win); @@ -1685,25 +2261,39 @@ void modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart *mime_part) { ModestMsgViewWindowPrivate *priv; + const gchar *msg_uid; + gchar *attachment_uid = NULL; + gint attachment_index = 0; + TnyList *attachments; + g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window)); g_return_if_fail (TNY_IS_MIME_PART (mime_part) || (mime_part == NULL)); - priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + msg_uid = modest_msg_view_window_get_message_uid (MODEST_MSG_VIEW_WINDOW (window)); + attachments = modest_msg_view_get_attachments (MODEST_MSG_VIEW (priv->msg_view)); + attachment_index = modest_list_index (attachments, (GObject *) mime_part); + g_object_unref (attachments); + + if (msg_uid && attachment_index >= 0) { + attachment_uid = g_strdup_printf ("%s/%d", msg_uid, attachment_index); + } + if (mime_part == NULL) { gboolean error = FALSE; - GList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view)); - if (selected_attachments == NULL) { + TnyList *selected_attachments = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view)); + if (selected_attachments == NULL || tny_list_get_length (selected_attachments) == 0) { error = TRUE; - } else if (g_list_length (selected_attachments) > 1) { + } else if (tny_list_get_length (selected_attachments) > 1) { hildon_banner_show_information (NULL, NULL, _("mcen_ib_unable_to_display_more")); error = TRUE; } else { - mime_part = (TnyMimePart *) selected_attachments->data; - g_object_ref (mime_part); + TnyIterator *iter; + iter = tny_list_create_iterator (selected_attachments); + mime_part = (TnyMimePart *) tny_iterator_get_current (iter); + g_object_unref (iter); } - g_list_foreach (selected_attachments, (GFunc) g_object_unref, NULL); - g_list_free (selected_attachments); + g_object_unref (selected_attachments); if (error) return; @@ -1713,34 +2303,54 @@ modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart if (tny_mime_part_is_purged (mime_part)) { g_object_unref (mime_part); - hildon_banner_show_information (NULL, NULL, _("mail_ib_attach_not_local")); return; } if (!TNY_IS_MSG (mime_part)) { gchar *filepath = NULL; const gchar *att_filename = tny_mime_part_get_filename (mime_part); - gchar *extension = NULL; + const gchar *content_type; + gboolean show_error_banner = FALSE; + GError *err; TnyFsStream *temp_stream = NULL; - - if (att_filename) { - extension = g_strrstr (att_filename, "."); - if (extension != NULL) - extension++; - } - - temp_stream = modest_maemo_utils_create_temp_stream (extension, &filepath); - - if (temp_stream) { - const gchar *content_type; + temp_stream = modest_utils_create_temp_stream (att_filename, attachment_uid, + &filepath); + + if (temp_stream != NULL) { content_type = tny_mime_part_get_content_type (mime_part); - tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream)); - - modest_platform_activate_file (filepath, content_type); + if (tny_mime_part_decode_to_stream (mime_part, TNY_STREAM (temp_stream), &err) >= 0) { + /* make the file read-only */ + if (g_chmod(filepath, 0444) != 0) + g_warning ("%s: failed to set file '%s' to read-only: %s", + __FUNCTION__, filepath, strerror(errno)); + + modest_platform_activate_file (filepath, content_type); + } else { + /* error while saving attachment, maybe cerm_device_memory_full */ + show_error_banner = TRUE; + if (err != NULL) { + g_warning ("%s: tny_mime_part_decode_to_stream failed (%s)", __FUNCTION__, err->message); + g_error_free (err); + } + } g_object_unref (temp_stream); g_free (filepath); - /* TODO: delete temporary file */ + /* NOTE: files in the temporary area will be automatically + * cleaned after some time if they are no longer in use */ + } else { + if (filepath != NULL) { + /* the file may already exist but it isn't writable, + * let's try to open it anyway */ + content_type = tny_mime_part_get_content_type (mime_part); + modest_platform_activate_file (filepath, content_type); + g_free (filepath); + } else { + g_warning ("%s: modest_utils_create_temp_stream failed", __FUNCTION__); + show_error_banner = TRUE; + } } + if (show_error_banner) + modest_platform_information_banner (NULL, NULL, _("mail_ib_file_operation_failed")); } else { /* message attachment */ TnyHeader *header = NULL; @@ -1761,13 +2371,14 @@ modest_msg_view_window_view_attachment (ModestMsgViewWindow *window, TnyMimePart g_warning ("window for is already being created"); } else { /* it's not found, so create a new window for it */ - modest_window_mgr_register_header (mgr, header); /* register the uid before building the window */ + modest_window_mgr_register_header (mgr, header, attachment_uid); /* register the uid before building the window */ gchar *account = g_strdup (modest_window_get_active_account (MODEST_WINDOW (window))); if (!account) account = modest_account_mgr_get_default_account (modest_runtime_get_account_mgr ()); - msg_win = modest_msg_view_window_new (TNY_MSG (mime_part), account, NULL); + msg_win = modest_msg_view_window_new_for_attachment (TNY_MSG (mime_part), account, attachment_uid); + modest_window_set_zoom (MODEST_WINDOW (msg_win), + modest_window_get_zoom (MODEST_WINDOW (window))); modest_window_mgr_register_window (mgr, msg_win); - gtk_window_set_transient_for (GTK_WINDOW (msg_win), GTK_WINDOW (window)); gtk_widget_show_all (GTK_WIDGET (msg_win)); } } @@ -1816,21 +2427,22 @@ static gboolean idle_save_mime_part_show_result (SaveMimePartInfo *info) { if (info->pairs != NULL) { - gdk_threads_enter (); - save_mime_parts_to_file_with_checks (info); - gdk_threads_leave (); + save_mime_part_to_file (info); } else { gboolean result; result = info->result; - gdk_threads_enter (); + /* This is a GDK lock because we are an idle callback and + * hildon_banner_show_information is or does Gtk+ code */ + + gdk_threads_enter (); /* CHECKED */ save_mime_part_info_free (info, TRUE); if (result) { hildon_banner_show_information (NULL, NULL, _CS("sfil_ib_saved")); } else { hildon_banner_show_information (NULL, NULL, _("mail_ib_file_operation_failed")); } - gdk_threads_leave (); + gdk_threads_leave (); /* CHECKED */ } return FALSE; @@ -1843,16 +2455,19 @@ save_mime_part_to_file (SaveMimePartInfo *info) GnomeVFSHandle *handle; TnyStream *stream; SaveMimePartPair *pair = (SaveMimePartPair *) info->pairs->data; + gboolean decode_result = TRUE; - result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0777); + result = gnome_vfs_create (&handle, pair->filename, GNOME_VFS_OPEN_WRITE, FALSE, 0644); if (result == GNOME_VFS_OK) { stream = tny_vfs_stream_new (handle); - tny_mime_part_decode_to_stream (pair->part, stream); + if (tny_mime_part_decode_to_stream (pair->part, stream, NULL) < 0) { + decode_result = FALSE; + } g_object_unref (G_OBJECT (stream)); g_object_unref (pair->part); g_slice_free (SaveMimePartPair, pair); info->pairs = g_list_delete_link (info->pairs, info->pairs); - info->result = TRUE; + info->result = decode_result; } else { save_mime_part_info_free (info, FALSE); info->result = FALSE; @@ -1865,14 +2480,22 @@ save_mime_part_to_file (SaveMimePartInfo *info) static void save_mime_parts_to_file_with_checks (SaveMimePartInfo *info) { - SaveMimePartPair *pair; gboolean is_ok = TRUE; - - pair = info->pairs->data; - if (modest_maemo_utils_file_exists (pair->filename)) { + gint replaced_files = 0; + const GList *files = info->pairs; + const GList *iter; + + for (iter = files; (iter != NULL) && (replaced_files < 2); iter = g_list_next(iter)) { + SaveMimePartPair *pair = iter->data; + if (modest_utils_file_exists (pair->filename)) { + replaced_files++; + } + } + if (replaced_files) { GtkWidget *confirm_overwrite_dialog; - confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, - _("emev_nc_replace_files")); + const gchar *message = (replaced_files == 1) ? + _FM("docm_nc_replace_file") : _FM("docm_nc_replace_multiple"); + confirm_overwrite_dialog = hildon_note_new_confirmation (NULL, message); if (gtk_dialog_run (GTK_DIALOG (confirm_overwrite_dialog)) != GTK_RESPONSE_OK) { is_ok = FALSE; } @@ -1882,16 +2505,19 @@ save_mime_parts_to_file_with_checks (SaveMimePartInfo *info) if (!is_ok) { save_mime_part_info_free (info, TRUE); } else { + GtkWidget *banner = hildon_banner_show_animation (NULL, NULL, + _CS("sfil_ib_saving")); + info->banner = g_object_ref (banner); g_thread_create ((GThreadFunc)save_mime_part_to_file, info, FALSE, NULL); + g_object_unref (banner); } } void -modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mime_parts) +modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, TnyList *mime_parts) { - gboolean clean_list = FALSE; ModestMsgViewWindowPrivate *priv; GList *files_to_save = NULL; GtkWidget *save_dialog = NULL; @@ -1905,24 +2531,29 @@ modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mim if (mime_parts == NULL) { mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view)); - if (mime_parts == NULL) + if (mime_parts == NULL || tny_list_get_length (mime_parts) == 0) return; - clean_list = TRUE; + } else { + g_object_ref (mime_parts); } /* prepare dialog */ - if (mime_parts->next == NULL) { + if (tny_list_get_length (mime_parts) == 1) { + TnyIterator *iter; /* only one attachment selected */ - TnyMimePart *mime_part = (TnyMimePart *) mime_parts->data; + iter = tny_list_create_iterator (mime_parts); + TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter); + g_object_unref (iter); if (!TNY_IS_MSG (mime_part) && tny_mime_part_is_attachment (mime_part)) { filename = tny_mime_part_get_filename (mime_part); } else { g_warning ("Tried to save a non-file attachment"); canceled = TRUE; } + g_object_unref (mime_part); } else { - save_multiple_str = g_strdup_printf (_("FIXME: %d attachments"), - g_list_length (mime_parts)); + save_multiple_str = g_strdup_printf (_FM("sfil_va_number_of_objects_attachments"), + tny_list_get_length (mime_parts)); } save_dialog = hildon_file_chooser_dialog_new (GTK_WINDOW (window), @@ -1947,66 +2578,87 @@ modest_msg_view_window_save_attachments (ModestMsgViewWindow *window, GList *mim if (gtk_dialog_run (GTK_DIALOG (save_dialog)) == GTK_RESPONSE_OK) { gchar *chooser_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (save_dialog)); - if (!modest_maemo_utils_folder_writable (chooser_uri)) { + if (!modest_utils_folder_writable (chooser_uri)) { hildon_banner_show_information (NULL, NULL, dgettext("hildon-fm", "sfil_ib_readonly_location")); } else { - GList *node = NULL; + TnyIterator *iter; - for (node = mime_parts; node != NULL; node = g_list_next (node)) { - TnyMimePart *mime_part = (TnyMimePart *) node->data; - + iter = tny_list_create_iterator (mime_parts); + while (!tny_iterator_is_done (iter)) { + TnyMimePart *mime_part = (TnyMimePart *) tny_iterator_get_current (iter); + + tny_iterator_next (iter); if (tny_mime_part_is_attachment (mime_part)) { SaveMimePartPair *pair; - if ((mime_parts->next != NULL) && - (tny_mime_part_get_filename (mime_part) == NULL)) + if ((!tny_iterator_is_done (iter)) && + (tny_mime_part_get_filename (mime_part) == NULL)) { + g_object_unref (mime_part); continue; + } pair = g_slice_new0 (SaveMimePartPair); - if (mime_parts->next == NULL) { + if (tny_iterator_is_done (iter)) { pair->filename = g_strdup (chooser_uri); } else { pair->filename = g_build_filename (chooser_uri, tny_mime_part_get_filename (mime_part), NULL); } - pair->part = g_object_ref (mime_part); + pair->part = mime_part; files_to_save = g_list_prepend (files_to_save, pair); } } + g_object_unref (iter); } g_free (chooser_uri); } gtk_widget_destroy (save_dialog); - if (clean_list) { - g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL); - g_list_free (mime_parts); - } + g_object_unref (mime_parts); if (files_to_save != NULL) { SaveMimePartInfo *info = g_slice_new0 (SaveMimePartInfo); - GtkWidget *banner = hildon_banner_show_animation (NULL, NULL, - _CS("sfil_ib_saving")); info->pairs = files_to_save; - info->banner = banner; info->result = TRUE; - g_object_ref (banner); save_mime_parts_to_file_with_checks (info); } } +static gboolean +show_remove_attachment_information (gpointer userdata) +{ + ModestMsgViewWindow *window = (ModestMsgViewWindow *) userdata; + ModestMsgViewWindowPrivate *priv = MODEST_MSG_VIEW_WINDOW_GET_PRIVATE (window); + + /* We're outside the main lock */ + gdk_threads_enter (); + + if (priv->remove_attachment_banner != NULL) { + gtk_widget_destroy (priv->remove_attachment_banner); + g_object_unref (priv->remove_attachment_banner); + } + + priv->remove_attachment_banner = g_object_ref ( + hildon_banner_show_animation (NULL, NULL, _("mcen_ib_removing_attachment"))); + + gdk_threads_leave (); + + return FALSE; +} + void modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean get_all) { ModestMsgViewWindowPrivate *priv; - GList *mime_parts = NULL, *node; + TnyList *mime_parts = NULL; gchar *confirmation_message; gint response; gint n_attachments; TnyMsg *msg; + TnyIterator *iter; /* TnyFolder *folder; */ g_return_if_fail (MODEST_IS_MSG_VIEW_WINDOW (window)); @@ -2018,37 +2670,42 @@ modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean mime_parts = modest_msg_view_get_selected_attachments (MODEST_MSG_VIEW (priv->msg_view)); /* Remove already purged messages from mime parts list */ - node = mime_parts; - while (node != NULL) { - TnyMimePart *part = TNY_MIME_PART (node->data); + iter = tny_list_create_iterator (mime_parts); + while (!tny_iterator_is_done (iter)) { + TnyMimePart *part = TNY_MIME_PART (tny_iterator_get_current (iter)); + tny_iterator_next (iter); if (tny_mime_part_is_purged (part)) { - GList *deleted_node = node; - node = g_list_next (node); - g_object_unref (part); - mime_parts = g_list_delete_link (mime_parts, deleted_node); - } else { - node = g_list_next (node); + tny_list_remove (mime_parts, (GObject *) part); } + g_object_unref (part); } + g_object_unref (iter); - if (mime_parts == NULL) + if (tny_list_get_length (mime_parts) == 0) { + g_object_unref (mime_parts); return; + } - n_attachments = g_list_length (mime_parts); + n_attachments = tny_list_get_length (mime_parts); if (n_attachments == 1) { const gchar *filename; + TnyMimePart *part; - if (TNY_IS_MSG (mime_parts->data)) { + iter = tny_list_create_iterator (mime_parts); + part = (TnyMimePart *) tny_iterator_get_current (iter); + g_object_unref (iter); + if (TNY_IS_MSG (part)) { TnyHeader *header; - header = tny_msg_get_header (TNY_MSG (mime_parts->data)); + header = tny_msg_get_header (TNY_MSG (part)); filename = tny_header_get_subject (header); g_object_unref (header); if (filename == NULL) filename = _("mail_va_no_subject"); } else { - filename = tny_mime_part_get_filename (TNY_MIME_PART (mime_parts->data)); + filename = tny_mime_part_get_filename (TNY_MIME_PART (part)); } confirmation_message = g_strdup_printf (_("mcen_nc_purge_file_text"), filename); + g_object_unref (part); } else { confirmation_message = g_strdup_printf (ngettext("mcen_nc_purge_file_text", "mcen_nc_purge_files_text", @@ -2058,28 +2715,47 @@ modest_msg_view_window_remove_attachments (ModestMsgViewWindow *window, gboolean confirmation_message); g_free (confirmation_message); - if (response != GTK_RESPONSE_OK) + if (response != GTK_RESPONSE_OK) { + g_object_unref (mime_parts); return; + } + priv->purge_timeout = g_timeout_add (2000, show_remove_attachment_information, window); /* folder = tny_msg_get_folder (msg); */ /* tny_msg_uncache_attachments (msg); */ /* tny_folder_refresh (folder, NULL); */ /* g_object_unref (folder); */ - modest_platform_information_banner (NULL, NULL, _("mcen_ib_removing_attachment")); + iter = tny_list_create_iterator (mime_parts); + while (!tny_iterator_is_done (iter)) { + TnyMimePart *part; - for (node = mime_parts; node != NULL; node = g_list_next (node)) { - tny_mime_part_set_purged (TNY_MIME_PART (node->data)); + part = (TnyMimePart *) tny_iterator_get_current (iter); + tny_mime_part_set_purged (TNY_MIME_PART (part)); /* modest_msg_view_remove_attachment (MODEST_MSG_VIEW (priv->msg_view), node->data); */ + g_object_unref (part); + tny_iterator_next (iter); } + g_object_unref (iter); - msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view)); - modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), NULL); + msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)); + tny_msg_view_clear (TNY_MSG_VIEW (priv->msg_view)); tny_msg_rewrite_cache (msg); - modest_msg_view_set_message (MODEST_MSG_VIEW (priv->msg_view), msg); + tny_msg_view_set_msg (TNY_MSG_VIEW (priv->msg_view), msg); + g_object_unref (msg); + + g_object_unref (mime_parts); + + if (priv->purge_timeout > 0) { + g_source_remove (priv->purge_timeout); + priv->purge_timeout = 0; + } - g_list_foreach (mime_parts, (GFunc) g_object_unref, NULL); - g_list_free (mime_parts); + if (priv->remove_attachment_banner) { + gtk_widget_destroy (priv->remove_attachment_banner); + g_object_unref (priv->remove_attachment_banner); + priv->remove_attachment_banner = NULL; + } } @@ -2092,8 +2768,9 @@ update_window_title (ModestMsgViewWindow *window) TnyMsg *msg = NULL; TnyHeader *header = NULL; const gchar *subject = NULL; + + msg = tny_msg_view_get_msg (TNY_MSG_VIEW (priv->msg_view)); - msg = modest_msg_view_get_message (MODEST_MSG_VIEW (priv->msg_view)); if (msg != NULL) { header = tny_msg_get_header (msg); subject = tny_header_get_subject (header); @@ -2105,3 +2782,12 @@ update_window_title (ModestMsgViewWindow *window) gtk_window_set_title (GTK_WINDOW (window), subject); } + + +static void on_move_focus (GtkWidget *widget, + GtkDirectionType direction, + gpointer userdata) +{ + g_signal_stop_emission_by_name (G_OBJECT (widget), "move-focus"); +} +