From 84c2681caafc791433135f704227111272e45203 Mon Sep 17 00:00:00 2001 From: Jose Dapena Paz Date: Mon, 30 Apr 2007 11:48:32 +0000 Subject: [PATCH] * src/modest-ui-actions.[ch]: * (..._on_delete) action simply closes the edit window, emitting a delete-event signal. * (..._on_close_window) also sends a delete-event. * (..._on_new_msg) adds the signature if there's one configured in the account. Also removed the "save to drafts" part just on creating the new message (fixes #NB55341, #NB55391). * (reply_forward_func) now gets the signature of the account. * (..._on_save_to_drafts). New action that saves an edited message as a draft. * src/modest-text-utils.[ch]: * (..._quote, ..._cite and ..._inline) now put the signature. * Added ..._convert_buffer_to_html. This way we can convert to html tags a subpart of a document (it does not add html headers). Also modified ..._convert_to_html to use this function. * Added ..._convert_to_html_body. It converts a string to an html that can be included inside a formatted html document. * src/maemo/modest-address-book.c: * (..._select_addresses): after selecting an address of a recipient editor through the select contacts dialog, focus is asigned to the ModestRecptEditor. * (select_email_addrs_for_contact): when the dialog is loaded, first address is selected by default. * src/maemo/modest-msg-edit-window.c: * Implemented dim rule: if focus is not in body or message format is plain text, font actions are dimmed. Also configured proper insensitive_press messages. * Implemented dim rule: if To: recipient field is empty, send action is dimmed. Also configured proper insensitive_press message. * Delete event handler. It shows a dialog for saving the mail as a draft if message is modified. * Fixed the html document parsing procedure, as it wasn't previously really parsed because the buffer length was not provided. * If the account setup specified that it should by default create plain text mails, it's used. Also radio menu option is updated properly. * Now we get correctly the focused widget. If "Select contacts" is selected, it adds contacts to the focused recipient list, or to To: field if no recipient list is focused. * If the To: field is empty on opening editor, focus goes to it. * Now the message stores a reference to the draft message used to create the mail. * Refactored the ..._select_contacts function to use the general "open_addressbook" function. * Added ..._is_modified and ..._reset_modified. It tells if the message was modified by the user. * src/maemo/ui/modest-msg-edit-window-ui.xml: * Added email menu options Send, Save to drafts, new message, delete. * Added close window and close all windows menu actions. * src/modest-formatter.[ch]: * Now the formatter adds the signature properly. * src/modest-mail-operation.[ch]: * Added ..._save_to_drafts operation. It creates a mail from editor and saves it to the active account drafts folder. * src/modest-defs.h: * Added configuration option for preferring formatted of plain text. * src/widgets/modest-recpt-view.[ch]: * Now button release event is processed after the default handler. * src/widgets/modest-recpt-editor.[ch]: * Completely new behavior for keyboard and mouse edition. Now only one address per line. Keyboard and mouse selection treat resolved addresses as a whole (you shouldn't be able to manipulate a part of resolved address). * Keyboard shortcut for adding addresses with the addressbook. * Better focus and cursor handling. * Now the widget does not use GTK_CAN_FOCUS. * (..._get_buffer): new API function to export the internal buffer. * (..._grab_focus): new function to make the textview grab the focus. * src/widgets/modest-scroll-text.[ch]: * Small fixes to calculation of the number of lines. Now it also makes the cursor visible. * src/widgets/modest-msg-edit-window-ui.h: * Added actions for "window close", "new message", "delete", "save to drafts", "all windows close" * src/modest-tny-msg.[ch]: * Support plain text quoted texts as input of msg editor. * Now (create_reply_forward_mail) takes the "prefer formatted" option. * Now it uses the logical id's for "Re:" and "Fwd:" strings. * Added signature support. * src/maemo/modest-main-window-ui.h: * Added a new action for new messages. It's used for showing a simple "New" menu option for creating new messages from the view (fixes #NB55368). pmo-trunk-r1720 --- src/maemo/easysetup/modest-easysetup-wizard.h | 1 + src/maemo/easysetup/modest-wizard-dialog.c | 7 +- src/maemo/modest-address-book.c | 10 + src/maemo/modest-main-window-ui.h | 1 + src/maemo/modest-msg-edit-window.c | 287 ++++++++++++++--- src/maemo/ui/modest-msg-edit-window-ui.xml | 12 + src/maemo/ui/modest-msg-view-window-ui.xml | 2 +- src/modest-defs.h | 6 +- src/modest-formatter.c | 10 +- src/modest-formatter.h | 2 +- src/modest-mail-operation.c | 56 ++++ src/modest-mail-operation.h | 31 ++ src/modest-text-utils.c | 90 ++++-- src/modest-text-utils.h | 14 + src/modest-tny-msg.c | 26 +- src/modest-tny-msg.h | 4 + src/modest-ui-actions.c | 114 ++++++- src/modest-ui-actions.h | 3 + src/widgets/modest-msg-edit-window-ui.h | 6 + src/widgets/modest-recpt-editor.c | 418 ++++++++++++++++++++++++- src/widgets/modest-recpt-editor.h | 3 + src/widgets/modest-recpt-view.c | 2 +- src/widgets/modest-scroll-text.c | 12 +- 23 files changed, 1005 insertions(+), 112 deletions(-) diff --git a/src/maemo/easysetup/modest-easysetup-wizard.h b/src/maemo/easysetup/modest-easysetup-wizard.h index 00e6df1..9c3cb80 100644 --- a/src/maemo/easysetup/modest-easysetup-wizard.h +++ b/src/maemo/easysetup/modest-easysetup-wizard.h @@ -9,6 +9,7 @@ /* #include */ #include "modest-wizard-dialog.h" /* We use a copied-and-improved HildonWizardDialog. */ #include "modest-account-mgr.h" +#include #ifdef HAVE_CONFIG_H #include diff --git a/src/maemo/easysetup/modest-wizard-dialog.c b/src/maemo/easysetup/modest-wizard-dialog.c index f856c64..2131caa 100644 --- a/src/maemo/easysetup/modest-wizard-dialog.c +++ b/src/maemo/easysetup/modest-wizard-dialog.c @@ -21,6 +21,7 @@ * widget provided by users contains the actual wizard pages. */ +#include #include #include #include @@ -234,12 +235,12 @@ init (ModestWizardDialog *wizard_dialog) gtk_dialog_set_has_separator (dialog, FALSE); wizard_dialog->priv = priv; priv->box = GTK_BOX (gtk_hbox_new (FALSE, 0)); -#ifdef MODEST_HILDON_VERSION_0 +#ifdef MODEST_HILDON_VERSION_0 priv->image = gtk_image_new_from_icon_name ("qgn_widg_wizard", - HILDON_ICON_SIZE_WIDG_WIZARD); + HILDON_ICON_SIZE_WIDG_WIZARD); #else priv->image = gtk_image_new_from_icon_name ("qgn_widg_wizard", - HILDON_ICON_SIZE_WIZARD); + HILDON_ICON_SIZE_WIZARD); #endif /*MODEST_HILDON_VERSION_0*/ /* Default values for user provided properties */ priv->notebook = NULL; diff --git a/src/maemo/modest-address-book.c b/src/maemo/modest-address-book.c index b28a51d..183034a 100644 --- a/src/maemo/modest-address-book.c +++ b/src/maemo/modest-address-book.c @@ -139,9 +139,13 @@ modest_address_book_select_addresses (ModestRecptEditor *recpt_editor) GtkWidget *contact_dialog; GSList *email_addrs_per_contact = NULL; gchar *econtact_id; + gboolean focus_recpt_editor = FALSE; + GtkWidget *toplevel; g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor)); + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (recpt_editor)); + contact_model = osso_abook_contact_model_new (); if (!open_addressbook ()) { if (contact_model) { @@ -179,6 +183,7 @@ modest_address_book_select_addresses (ModestRecptEditor *recpt_editor) g_slist_foreach (email_addrs_per_contact, (GFunc) g_free, NULL); g_slist_free (email_addrs_per_contact); email_addrs_per_contact = NULL; + focus_recpt_editor = TRUE; } } g_list_free (contacts_list); @@ -196,6 +201,9 @@ modest_address_book_select_addresses (ModestRecptEditor *recpt_editor) gtk_widget_destroy (contact_dialog); + if (focus_recpt_editor) + modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (recpt_editor)); + } /** @@ -512,6 +520,8 @@ select_email_addrs_for_contact(GList * email_addr_list) gtk_list_store_set(list_store, &iter, 0, email_addr, -1); g_free(email_addr); } + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter); + gtk_tree_selection_select_iter (selection, &iter); gtk_widget_show_all(select_email_addr_dlg); result = gtk_dialog_run(GTK_DIALOG(select_email_addr_dlg)); diff --git a/src/maemo/modest-main-window-ui.h b/src/maemo/modest-main-window-ui.h index a3d27c6..69841f5 100644 --- a/src/maemo/modest-main-window-ui.h +++ b/src/maemo/modest-main-window-ui.h @@ -55,6 +55,7 @@ static const GtkActionEntry modest_action_entries [] = { /* Email */ { "EmailNew", NULL, N_("mcen_me_inbox_new") }, /* submenu */ { "EmailNewMessage", NULL, N_("mcen_me_inbox_message"), "N", NULL, G_CALLBACK (modest_ui_actions_on_new_msg) }, + { "EmailNewDefault", NULL, N_("mcen_me_inbox_new"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_new_msg) }, { "EmailNewFolder", NULL, N_("mcen_me_inbox_folder"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_new_folder) }, { "EmailOpen", NULL, N_("mcen_me_inbox_open"), "O", NULL, NULL }, { "EmailReply", NULL, N_("mcen_me_inbox_reply"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_reply) }, diff --git a/src/maemo/modest-msg-edit-window.c b/src/maemo/modest-msg-edit-window.c index fe64bf1..da30d63 100644 --- a/src/maemo/modest-msg-edit-window.c +++ b/src/maemo/modest-msg-edit-window.c @@ -55,6 +55,7 @@ #include "modest-tny-platform-factory.h" #include "modest-tny-msg.h" #include "modest-address-book.h" +#include "modest-text-utils.h" #include #include #include @@ -75,6 +76,14 @@ static void modest_msg_edit_window_class_init (ModestMsgEditWindowClass *klas static void modest_msg_edit_window_init (ModestMsgEditWindow *obj); static void modest_msg_edit_window_finalize (GObject *obj); +static gboolean msg_body_focus (GtkWidget *focus, GdkEventFocus *event, gpointer userdata); +static void to_field_changed (GtkTextBuffer *buffer, ModestMsgEditWindow *editor); +static void send_insensitive_press (GtkWidget *widget, ModestMsgEditWindow *editor); +static void style_insensitive_press (GtkWidget *widget, ModestMsgEditWindow *editor); +static void setup_insensitive_handlers (ModestMsgEditWindow *editor); +static void reset_modified (ModestMsgEditWindow *editor); +static gboolean is_modified (ModestMsgEditWindow *editor); + static void text_buffer_refresh_attributes (WPTextBuffer *buffer, ModestMsgEditWindow *window); static void text_buffer_mark_set (GtkTextBuffer *buffer, GtkTextIter *location, GtkTextMark *mark, gpointer userdata); static void text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWindow *window); @@ -143,6 +152,8 @@ struct _ModestMsgEditWindowPrivate { TnyHeaderFlags priority_flags; gdouble zoom_level; + + TnyMsg *draft_msg; }; #define MODEST_MSG_EDIT_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ @@ -220,6 +231,8 @@ modest_msg_edit_window_init (ModestMsgEditWindow *obj) priv->bcc_caption = NULL; priv->priority_flags = 0; + + priv->draft_msg = NULL; } @@ -399,6 +412,14 @@ init_window (ModestMsgEditWindow *obj) g_signal_connect_swapped (G_OBJECT (priv->bcc_field), "open-addressbook", G_CALLBACK (modest_msg_edit_window_open_addressbook), obj); + g_signal_connect (G_OBJECT (priv->msg_body), "focus-in-event", + G_CALLBACK (msg_body_focus), obj); + g_signal_connect (G_OBJECT (priv->msg_body), "focus-out-event", + G_CALLBACK (msg_body_focus), obj); + g_signal_connect (G_OBJECT (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field))), + "changed", G_CALLBACK (to_field_changed), obj); + to_field_changed (modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR (priv->to_field)), MODEST_MSG_EDIT_WINDOW (obj)); + priv->scroll = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scroll), GTK_SHADOW_NONE); @@ -438,8 +459,39 @@ modest_msg_edit_window_finalize (GObject *obj) static gboolean on_delete_event (GtkWidget *widget, GdkEvent *event, ModestMsgEditWindow *self) { + GtkWidget *close_dialog; + ModestMsgEditWindowPrivate *priv; + gint response; + + priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (self); save_settings (self); - return FALSE; + if (is_modified (self)) { + close_dialog = hildon_note_new_confirmation (GTK_WINDOW (self), _("mcen_nc_no_email_message_modified_save_changes")); + response = gtk_dialog_run (GTK_DIALOG (close_dialog)); + gtk_widget_destroy (close_dialog); + + if (response != GTK_RESPONSE_CANCEL) { + modest_ui_actions_on_save_to_drafts (NULL, self); + } + } +/* /\* remove old message from drafts *\/ */ +/* if (priv->draft_msg) { */ +/* TnyHeader *header = tny_msg_get_header (priv->draft_msg); */ +/* TnyAccount *account = modest_tny_account_store_get_tny_account_by_account (modest_runtime_get_account_store(), */ +/* account_name, */ +/* TNY_ACCOUNT_TYPE_STORE); */ +/* TnyFolder *folder = modest_tny_account_get_special_folder (account, TNY_FOLDER_TYPE_DRAFTS); */ +/* g_return_val_if_fail (TNY_IS_HEADER (header), FALSE); */ +/* g_return_val_if_fail (TNY_IS_FOLDER (folder), FALSE); */ +/* tny_folder_remove_msg (folder, header, NULL); */ +/* g_object_unref (folder); */ +/* g_object_unref (header); */ +/* g_object_unref (priv->draft_msg); */ +/* priv->draft_msg = NULL; */ +/* } */ + gtk_widget_destroy (GTK_WIDGET (self)); + + return TRUE; } static GtkWidget * @@ -472,8 +524,10 @@ static void set_msg (ModestMsgEditWindow *self, TnyMsg *msg) { TnyHeader *header; - const gchar *to, *cc, *bcc, *subject, *body; + const gchar *to, *cc, *bcc, *subject; + gchar *body; ModestMsgEditWindowPrivate *priv; + GtkTextIter iter; g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (self)); g_return_if_fail (TNY_IS_MSG (msg)); @@ -498,30 +552,26 @@ set_msg (ModestMsgEditWindow *self, TnyMsg *msg) /* gtk_text_buffer_set_can_paste_rich_text (priv->text_buffer, TRUE); */ wp_text_buffer_reset_buffer (WP_TEXT_BUFFER (priv->text_buffer), TRUE); body = modest_tny_msg_get_body (msg, FALSE); - if ((body!=NULL) && (body[0] != '\0')) { - wp_text_buffer_load_document_begin (WP_TEXT_BUFFER (priv->text_buffer), TRUE); - wp_text_buffer_load_document_write (WP_TEXT_BUFFER (priv->text_buffer), - (gchar *) body, - -1); - wp_text_buffer_load_document_end (WP_TEXT_BUFFER (priv->text_buffer)); - } else { - WPTextBufferFormat fmt = {0}; - - fmt.font_size = DEFAULT_FONT_SIZE; - fmt.font = DEFAULT_FONT; - fmt.rich_text = 1; - fmt.text_position = TEXT_POSITION_NORMAL; - fmt.justification = 0; - fmt.cs.font_size = 1; - fmt.cs.font = 1; - fmt.cs.text_position = 1; - fmt.cs.justification = 1; - wp_text_buffer_set_format (WP_TEXT_BUFFER (priv->text_buffer), &fmt); + + if ((body == NULL)||(body[0] == '\0')) { + g_free (body); + body = modest_text_utils_convert_to_html (""); + } + wp_text_buffer_load_document_begin (WP_TEXT_BUFFER (priv->text_buffer), TRUE); + wp_text_buffer_load_document_write (WP_TEXT_BUFFER (priv->text_buffer), + (gchar *) body, + strlen (body)); + wp_text_buffer_load_document_end (WP_TEXT_BUFFER (priv->text_buffer)); + g_free (body); + + /* Get the default format required from configuration */ + if (!modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL)) { + wp_text_buffer_enable_rich_text (WP_TEXT_BUFFER (priv->text_buffer), FALSE); } /* Set the default focus depending on having already a To: field or not */ if ((!to)||(*to == '\0')) { - gtk_widget_grab_focus (priv->to_field); + modest_recpt_editor_grab_focus (MODEST_RECPT_EDITOR (priv->to_field)); } else { gtk_widget_grab_focus (priv->msg_body); } @@ -535,8 +585,15 @@ set_msg (ModestMsgEditWindow *self, TnyMsg *msg) if (priv->attachments == NULL) gtk_widget_hide_all (priv->attachments_caption); + gtk_text_buffer_get_start_iter (priv->text_buffer, &iter); + gtk_text_buffer_place_cursor (priv->text_buffer, &iter); + + reset_modified (self); + update_dimmed (self); text_buffer_can_undo (priv->text_buffer, FALSE, self); + + priv->draft_msg = msg; } static void @@ -762,7 +819,7 @@ modest_msg_edit_window_new (TnyMsg *msg, const gchar *account_name) gtk_action_group_add_radio_actions (action_group, modest_msg_edit_file_format_action_entries, G_N_ELEMENTS (modest_msg_edit_file_format_action_entries), - MODEST_FILE_FORMAT_FORMATTED_TEXT, + modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL), G_CALLBACK (modest_ui_actions_msg_edit_on_change_file_format), obj); gtk_ui_manager_insert_action_group (parent_priv->ui_manager, action_group, 0); @@ -799,6 +856,8 @@ modest_msg_edit_window_new (TnyMsg *msg, const gchar *account_name) modest_msg_edit_window_setup_toolbar (MODEST_MSG_EDIT_WINDOW (obj)); + setup_insensitive_handlers (MODEST_MSG_EDIT_WINDOW (obj)); + set_msg (MODEST_MSG_EDIT_WINDOW (obj), msg); text_buffer_refresh_attributes (WP_TEXT_BUFFER (priv->text_buffer), MODEST_MSG_EDIT_WINDOW (obj)); @@ -1537,12 +1596,26 @@ modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window, GtkWidget *view_focus; view_focus = gtk_window_get_focus (GTK_WINDOW (window)); - if (gtk_widget_get_parent (view_focus) && - MODEST_IS_RECPT_EDITOR (gtk_widget_get_parent (view_focus))) { - editor = MODEST_RECPT_EDITOR (gtk_widget_get_parent (view_focus)); - } else { - editor = MODEST_RECPT_EDITOR (priv->to_field); + /* This code should be kept in sync with ModestRecptEditor. The + textview inside the recpt editor is the one that really gets the + focus. As it's inside a scrolled window, and this one inside the + hbox recpt editor inherits from, we'll need to go up in the + hierarchy to know if the text view is part of the recpt editor + or if it's a different text entry */ + + if (gtk_widget_get_parent (view_focus)) { + GtkWidget *first_parent; + + first_parent = gtk_widget_get_parent (view_focus); + if (gtk_widget_get_parent (first_parent) && + MODEST_IS_RECPT_EDITOR (gtk_widget_get_parent (first_parent))) { + editor = MODEST_RECPT_EDITOR (gtk_widget_get_parent (first_parent)); + } } + + if (editor == NULL) + editor = MODEST_RECPT_EDITOR (priv->to_field); + } modest_address_book_select_addresses (editor); @@ -1552,20 +1625,11 @@ modest_msg_edit_window_open_addressbook (ModestMsgEditWindow *window, void modest_msg_edit_window_select_contacts (ModestMsgEditWindow *window) { - GtkWidget *focused; - ModestMsgEditWindowPrivate *priv; - g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW (window)); - priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); - focused = gtk_window_get_focus (GTK_WINDOW (window)); - if (MODEST_IS_RECPT_EDITOR (focused)) { - modest_msg_edit_window_open_addressbook (window, MODEST_RECPT_EDITOR (focused)); - } else { - modest_msg_edit_window_open_addressbook (window, MODEST_RECPT_EDITOR (priv->to_field)); - } - + modest_msg_edit_window_open_addressbook (window, NULL); } + static void modest_msg_edit_window_show_toolbar (ModestWindow *self, gboolean show_toolbar) @@ -1806,33 +1870,74 @@ update_dimmed (ModestMsgEditWindow *window) GtkAction *action; GtkWidget *widget; gboolean rich_text; + gboolean editor_focused; rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer)); + editor_focused = gtk_widget_is_focus (priv->msg_body); action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/SelectFontMenu"); - gtk_action_set_sensitive (action, rich_text); + gtk_action_set_sensitive (action, rich_text && editor_focused); action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/BulletedListMenu"); - gtk_action_set_sensitive (action, rich_text); + gtk_action_set_sensitive (action, rich_text && editor_focused); action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu"); - gtk_action_set_sensitive (action, rich_text); + gtk_action_set_sensitive (action, rich_text && editor_focused); action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentLeftMenu"); - gtk_action_set_sensitive (action, rich_text); + gtk_action_set_sensitive (action, rich_text && editor_focused); action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentCenterMenu"); - gtk_action_set_sensitive (action, rich_text); + gtk_action_set_sensitive (action, rich_text && editor_focused); action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentRightMenu"); - gtk_action_set_sensitive (action, rich_text); + gtk_action_set_sensitive (action, rich_text && editor_focused); action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/AttachmentsMenu/InsertImageMenu"); - gtk_action_set_sensitive (action, rich_text); + gtk_action_set_sensitive (action, rich_text && editor_focused); action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsBold"); - gtk_action_set_sensitive (action, rich_text); + gtk_action_set_sensitive (action, rich_text && editor_focused); action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ActionsItalics"); - gtk_action_set_sensitive (action, rich_text); + gtk_action_set_sensitive (action, rich_text && editor_focused); + widget = priv->font_color_button; + gtk_widget_set_sensitive (widget, rich_text && editor_focused); + widget = priv->font_size_toolitem; + gtk_widget_set_sensitive (widget, rich_text && editor_focused); + widget = priv->font_face_toolitem; + gtk_widget_set_sensitive (widget, rich_text && editor_focused); +} + +static void +setup_insensitive_handlers (ModestMsgEditWindow *window) +{ + ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (window); + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (window); + GtkWidget *widget; + + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ToolbarSend"); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (send_insensitive_press), window); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/MenuBar/EmailMenu/SendMenu"); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (send_insensitive_press), window); + + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/MenuBar/FormatMenu/SelectFontMenu"); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (style_insensitive_press), window); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/MenuBar/FormatMenu/BulletedListMenu"); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (style_insensitive_press), window); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu"); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (style_insensitive_press), window); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentLeftMenu"); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (style_insensitive_press), window); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentCenterMenu"); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (style_insensitive_press), window); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/MenuBar/FormatMenu/AlignmentMenu/AlignmentRightMenu"); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (style_insensitive_press), window); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/MenuBar/AttachmentsMenu/InsertImageMenu"); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (style_insensitive_press), window); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsBold"); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (style_insensitive_press), window); + widget = gtk_ui_manager_get_widget (parent_priv->ui_manager, "/ToolBar/ActionsItalics"); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (style_insensitive_press), window); widget = priv->font_color_button; - gtk_widget_set_sensitive (widget, rich_text); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (style_insensitive_press), window); widget = priv->font_size_toolitem; - gtk_widget_set_sensitive (widget, rich_text); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (style_insensitive_press), window); widget = priv->font_face_toolitem; - gtk_widget_set_sensitive (widget, rich_text); + g_signal_connect (G_OBJECT (widget), "insensitive-press", G_CALLBACK (style_insensitive_press), window); + } static void @@ -1844,3 +1949,83 @@ text_buffer_can_undo (GtkTextBuffer *buffer, gboolean can_undo, ModestMsgEditWin action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EditMenu/UndoMenu"); gtk_action_set_sensitive (action, can_undo); } + +static gboolean +msg_body_focus (GtkWidget *focus, + GdkEventFocus *event, + gpointer userdata) +{ + update_dimmed (MODEST_MSG_EDIT_WINDOW (userdata)); + return FALSE; +} + +static void +to_field_changed (GtkTextBuffer *buffer, + ModestMsgEditWindow *editor) +{ + ModestWindowPrivate *parent_priv = MODEST_WINDOW_GET_PRIVATE (editor); + GtkAction *action; + + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/ToolBar/ToolbarSend"); + gtk_action_set_sensitive (action, gtk_text_buffer_get_char_count (buffer) != 0); + action = gtk_ui_manager_get_action (parent_priv->ui_manager, "/MenuBar/EmailMenu/SendMenu"); + gtk_action_set_sensitive (action, gtk_text_buffer_get_char_count (buffer) != 0); +} + +static void +send_insensitive_press (GtkWidget *widget, ModestMsgEditWindow *editor) +{ + hildon_banner_show_information (NULL, NULL, _("mcen_ib_add_recipients_first")); +} + +static void +style_insensitive_press (GtkWidget *widget, ModestMsgEditWindow *editor) +{ + gboolean rich_text, editor_focused; + + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor); + rich_text = wp_text_buffer_is_rich_text (WP_TEXT_BUFFER (priv->text_buffer)); + editor_focused = gtk_widget_is_focus (priv->msg_body); + + if (!rich_text) + hildon_banner_show_information (NULL, NULL, _("mcen_ib_item_unavailable_plaintext")); + else if (!editor_focused) + hildon_banner_show_information (NULL, NULL, _("mcen_ib_move_cursor_to_message")); +} + +static void +reset_modified (ModestMsgEditWindow *editor) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor); + GtkTextBuffer *buffer; + + buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field)); + gtk_text_buffer_set_modified (buffer, FALSE); + buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field)); + gtk_text_buffer_set_modified (buffer, FALSE); + buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field)); + gtk_text_buffer_set_modified (buffer, FALSE); + gtk_text_buffer_set_modified (priv->text_buffer, FALSE); +} + +static gboolean +is_modified (ModestMsgEditWindow *editor) +{ + ModestMsgEditWindowPrivate *priv = MODEST_MSG_EDIT_WINDOW_GET_PRIVATE (editor); + GtkTextBuffer *buffer; + + buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->to_field)); + if (gtk_text_buffer_get_modified (buffer)) + return TRUE; + buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->cc_field)); + if (gtk_text_buffer_get_modified (buffer)) + return TRUE; + buffer = modest_recpt_editor_get_buffer (MODEST_RECPT_EDITOR(priv->bcc_field)); + if (gtk_text_buffer_get_modified (buffer)) + return TRUE; + if (gtk_text_buffer_get_modified (priv->text_buffer)) + return TRUE; + + return FALSE; +} + diff --git a/src/maemo/ui/modest-msg-edit-window-ui.xml b/src/maemo/ui/modest-msg-edit-window-ui.xml index 94b93d0..a156210 100644 --- a/src/maemo/ui/modest-msg-edit-window-ui.xml +++ b/src/maemo/ui/modest-msg-edit-window-ui.xml @@ -33,7 +33,14 @@ + + + + + + + @@ -92,6 +99,11 @@ + + + + + diff --git a/src/maemo/ui/modest-msg-view-window-ui.xml b/src/maemo/ui/modest-msg-view-window-ui.xml index 6c8531e..86517fe 100644 --- a/src/maemo/ui/modest-msg-view-window-ui.xml +++ b/src/maemo/ui/modest-msg-view-window-ui.xml @@ -37,7 +37,7 @@ - + diff --git a/src/modest-defs.h b/src/modest-defs.h index 4d2a1a0..f0da7d9 100644 --- a/src/modest-defs.h +++ b/src/modest-defs.h @@ -60,6 +60,8 @@ #define MODEST_CONF_REPLY_TYPE MODEST_CONF_NAMESPACE "/reply_type" /* int */ #define MODEST_CONF_FORWARD_TYPE MODEST_CONF_NAMESPACE "/forward_type" /* int */ +#define MODEST_CONF_PREFER_FORMATTED_TEXT MODEST_CONF_NAMESPACE "/prefer_formatted_text" + #define MODEST_CONF_CONNECT_AT_STARTUP MODEST_CONF_NAMESPACE "/connect_at_startup" #define MODEST_CONF_SHOW_CC MODEST_CONF_NAMESPACE "/show_cc" @@ -89,8 +91,8 @@ #define MODEST_ACCOUNT_DISPLAY_NAME "display_name" /* string */ #define MODEST_ACCOUNT_STORE_ACCOUNT "store_account" /* string */ #define MODEST_ACCOUNT_TRANSPORT_ACCOUNT "transport_account" /* string */ -#define MODEST_ACCOUNT_FULLNAME "fullname" -#define MODEST_ACCOUNT_EMAIL "email" +#define MODEST_ACCOUNT_FULLNAME "fullname" /* string */ +#define MODEST_ACCOUNT_EMAIL "email" /* string */ /* This is a list of strings, with each strings, * alernating between a connection name, followed by a corresponding server account name. diff --git a/src/modest-formatter.c b/src/modest-formatter.c index ff73f89..8fcbf0d 100644 --- a/src/modest-formatter.c +++ b/src/modest-formatter.c @@ -41,6 +41,7 @@ typedef struct _ModestFormatterPrivate ModestFormatterPrivate; struct _ModestFormatterPrivate { gchar *content_type; + gchar *signature; }; #define MODEST_FORMATTER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \ MODEST_TYPE_FORMATTER, \ @@ -197,7 +198,7 @@ modest_formatter_attach (ModestFormatter *self, TnyMimePart *body, TnyHeader *he } ModestFormatter* -modest_formatter_new (const gchar *content_type) +modest_formatter_new (const gchar *content_type, const gchar *signature) { ModestFormatter *formatter; ModestFormatterPrivate *priv; @@ -205,6 +206,7 @@ modest_formatter_new (const gchar *content_type) formatter = g_object_new (MODEST_TYPE_FORMATTER, NULL); priv = MODEST_FORMATTER_GET_PRIVATE (formatter); priv->content_type = g_strdup (content_type); + priv->signature = g_strdup (signature); return formatter; } @@ -227,6 +229,9 @@ modest_formatter_finalize (GObject *object) if (priv->content_type) g_free (priv->content_type); + if (priv->signature) + g_free (priv->signature); + (*parent_class->finalize) (object); } @@ -278,6 +283,7 @@ modest_formatter_wrapper_cite (ModestFormatter *self, const gchar *text, TnyHead return modest_text_utils_cite (text, priv->content_type, + priv->signature, tny_header_get_from (header), tny_header_get_date_sent (header)); } @@ -289,6 +295,7 @@ modest_formatter_wrapper_inline (ModestFormatter *self, const gchar *text, TnyHe return modest_text_utils_inline (text, priv->content_type, + priv->signature, tny_header_get_from (header), tny_header_get_date_sent (header), tny_header_get_to (header), @@ -303,6 +310,7 @@ modest_formatter_wrapper_quote (ModestFormatter *self, const gchar *text, TnyHea /* TODO: get 80 from the configuration */ return modest_text_utils_quote (text, priv->content_type, + priv->signature, tny_header_get_from (header), tny_header_get_date_sent (header), 80); diff --git a/src/modest-formatter.h b/src/modest-formatter.h index cf5c040..b8c5a92 100644 --- a/src/modest-formatter.h +++ b/src/modest-formatter.h @@ -58,7 +58,7 @@ struct _ModestFormatterClass GType modest_formatter_get_type (void); -ModestFormatter* modest_formatter_new (const gchar *content_type); +ModestFormatter* modest_formatter_new (const gchar *content_type, const gchar *signature); /** * modest_formatter_cite: diff --git a/src/modest-mail-operation.c b/src/modest-mail-operation.c index a60c025..2d24e88 100644 --- a/src/modest-mail-operation.c +++ b/src/modest-mail-operation.c @@ -292,6 +292,62 @@ modest_mail_operation_send_new_mail (ModestMailOperation *self, g_object_unref (G_OBJECT (new_msg)); } +void +modest_mail_operation_save_to_drafts (ModestMailOperation *self, + TnyTransportAccount *transport_account, + const gchar *from, const gchar *to, + const gchar *cc, const gchar *bcc, + const gchar *subject, const gchar *plain_body, + const gchar *html_body, + const GList *attachments_list, + TnyHeaderFlags priority_flags) +{ + TnyMsg *msg = NULL; + TnyFolder *folder = NULL; + ModestMailOperationPrivate *priv = NULL; + GError *err = NULL; + + /* GList *node = NULL; */ + + g_return_if_fail (MODEST_IS_MAIL_OPERATION (self)); + g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account)); + + priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self); + + if (html_body == NULL) { + msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */ + } else { + msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list); + } + if (!msg) { + g_printerr ("modest: failed to create a new msg\n"); + goto cleanup; + } + + folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS); + if (!folder) { + g_printerr ("modest: failed to find Drafts folder\n"); + goto cleanup; + } + + tny_folder_add_msg (folder, msg, &err); + if (err) { + g_printerr ("modest: error adding msg to Drafts folder: %s", + err->message); + g_error_free (err); + goto cleanup; + } + + modest_mail_operation_queue_remove (modest_runtime_get_mail_operation_queue (), self); + + /* Free */ +cleanup: + if (msg) + g_object_unref (G_OBJECT(msg)); + if (folder) + g_object_unref (G_OBJECT(folder)); +} + static void recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders) { diff --git a/src/modest-mail-operation.h b/src/modest-mail-operation.h index e7e4081..d2739b9 100644 --- a/src/modest-mail-operation.h +++ b/src/modest-mail-operation.h @@ -150,6 +150,37 @@ void modest_mail_operation_send_new_mail (ModestMailOperation *self, const GList *attachments_list, TnyHeaderFlags priority_flags); + +/** + * modest_mail_operation_save_to_drafts: + * @self: a #ModestMailOperation + * @transport_account: a non-NULL #TnyTransportAccount + * @from: the email address of the mail sender + * @to: a non-NULL email address of the mail receiver + * @cc: a comma-separated list of email addresses where to send a carbon copy + * @bcc: a comma-separated list of email addresses where to send a blind carbon copy + * @subject: the subject of the new mail + * @plain_body: the plain text body of the new mail. + * @html_body: the html version of the body of the new mail. If NULL, the mail will + * be sent with the plain body only. + * @attachments_list: a #GList of attachments, each attachment must be a #TnyMimePart + * + * Save a mail message to drafts using the provided + * #TnyTransportAccount. This operation is synchronous, so the + * #ModestMailOperation should not be added to any + * #ModestMailOperationQueue + **/ +void modest_mail_operation_save_to_drafts (ModestMailOperation *self, + TnyTransportAccount *transport_account, + const gchar *from, + const gchar *to, + const gchar *cc, + const gchar *bcc, + const gchar *subject, + const gchar *plain_body, + const gchar *html_body, + const GList *attachments_list, + TnyHeaderFlags priority_flags); /** * modest_mail_operation_update_account: * @self: a #ModestMailOperation diff --git a/src/modest-text-utils.c b/src/modest-text-utils.c index 243ede3..60ee401 100644 --- a/src/modest-text-utils.c +++ b/src/modest-text-utils.c @@ -114,6 +114,7 @@ static gchar* modest_text_utils_quote_html (const gchar *text, gchar * modest_text_utils_quote (const gchar *text, const gchar *content_type, + const gchar *signature, const gchar *from, const time_t sent_date, int limit) @@ -141,16 +142,27 @@ modest_text_utils_quote (const gchar *text, gchar * modest_text_utils_cite (const gchar *text, const gchar *content_type, + const gchar *signature, const gchar *from, time_t sent_date) { gchar *tmp, *retval; + gchar *tmp_sig; g_return_val_if_fail (text, NULL); g_return_val_if_fail (content_type, NULL); + if (!signature) + tmp_sig = g_strdup (""); + else if (!strcmp(content_type, "text/html")) { + tmp_sig = modest_text_utils_convert_to_html_body(signature); + } else { + tmp_sig = g_strdup (signature); + } + tmp = cite (sent_date, from); - retval = g_strdup_printf ("%s%s\n", tmp, text); + retval = g_strdup_printf ("%s%s%s\n", tmp_sig, tmp, text); + g_free (tmp_sig); g_free (tmp); return retval; @@ -159,15 +171,17 @@ modest_text_utils_cite (const gchar *text, gchar * modest_text_utils_inline (const gchar *text, const gchar *content_type, + const gchar *signature, const gchar *from, time_t sent_date, const gchar *to, const gchar *subject) { gchar sent_str[101]; - const gchar *plain_format = "%s\n%s %s\n%s %s\n%s %s\n%s %s\n\n%s"; + gchar *formatted_signature; + const gchar *plain_format = "%s%s\n%s %s\n%s %s\n%s %s\n%s %s\n\n%s"; const gchar *html_format = \ - "%s
\n\n" \ + "%s%s
\n
\n" \ "\n" \ "\n" \ "\n" \ @@ -188,7 +202,17 @@ modest_text_utils_inline (const gchar *text, else format = plain_format; - return g_strdup_printf (format, + if (signature != NULL) { + if (!strcmp (content_type, "text/html")) { + formatted_signature = g_strconcat (signature, "
", NULL); + } else { + formatted_signature = g_strconcat (signature, "\n"); + } + } else { + formatted_signature = ""; + } + + return g_strdup_printf (format, formatted_signature, FORWARD_STRING, FROM_STRING, (from) ? from : EMPTY_STRING, SENT_STRING, sent_str, @@ -272,27 +296,15 @@ modest_text_utils_remove_address (const gchar *address_list, const gchar *addres return result; } - -gchar* -modest_text_utils_convert_to_html (const gchar *data) +static void +modest_text_utils_convert_buffer_to_html (GString *html, const gchar *data) { guint i; gboolean space_seen = FALSE; - GString *html; gsize len; - - if (!data) - return NULL; len = strlen (data); - html = g_string_sized_new (1.5 * len); /* just a guess... */ - g_string_append_printf (html, - "" - "" - "" - ""); - /* replace with special html chars where needed*/ for (i = 0; i != len; ++i) { char kar = data[i]; @@ -322,8 +334,48 @@ modest_text_utils_convert_to_html (const gchar *data) g_string_append_c (html, kar); } } +} + +gchar* +modest_text_utils_convert_to_html (const gchar *data) +{ + GString *html; + gsize len; - g_string_append (html, ""); + if (!data) + return NULL; + + len = strlen (data); + html = g_string_sized_new (1.5 * len); /* just a guess... */ + + g_string_append_printf (html, + "" + "" + "" + ""); + + modest_text_utils_convert_buffer_to_html (html, data); + + g_string_append (html, ""); + hyperlinkify_plain_text (html); + + return g_string_free (html, FALSE); +} + +gchar * +modest_text_utils_convert_to_html_body (const gchar *data) +{ + GString *html; + gsize len; + + if (!data) + return NULL; + + len = strlen (data); + html = g_string_sized_new (1.5 * len); /* just a guess... */ + + modest_text_utils_convert_buffer_to_html (html, data); + hyperlinkify_plain_text (html); return g_string_free (html, FALSE); diff --git a/src/modest-text-utils.h b/src/modest-text-utils.h index ffe623f..0a047f4 100644 --- a/src/modest-text-utils.h +++ b/src/modest-text-utils.h @@ -56,6 +56,7 @@ gchar* modest_text_utils_derived_subject (const gchar *subject, * @text: a non-NULL string which contains the message to quote * @from: a non-NULL sender of the original message * @content_type: the non-NULL content type for the quoting, e.g. "text/html" + * @signature: NULL or the signature to add * @sent_date: sent date/time of the original message * @limit: specifies the maximum characters per line in the quoted text * @@ -65,6 +66,7 @@ gchar* modest_text_utils_derived_subject (const gchar *subject, */ gchar* modest_text_utils_quote (const gchar *text, const gchar *content_type, + const gchar *signature, const gchar *from, const time_t sent_date, int limit); @@ -82,6 +84,7 @@ gchar* modest_text_utils_quote (const gchar *text, */ gchar* modest_text_utils_cite (const gchar *text, const gchar *content_type, + const gchar *signature, const gchar *from, time_t sent_date); @@ -100,6 +103,7 @@ gchar* modest_text_utils_cite (const gchar *text, */ gchar* modest_text_utils_inline (const gchar *text, const gchar *content_type, + const gchar *signature, const gchar *from, time_t sent_date, const gchar *to, @@ -147,6 +151,16 @@ void modest_text_utils_address_range_at_position (const gchar *recipients_li */ gchar* modest_text_utils_convert_to_html (const gchar *txt); +/** + * modest_text_utils_convert_to_html_body: + * @txt: a string + * + * convert plain text (utf8) into html without adding html headers. + * + * Returns: a newly allocated string containing the html + */ +gchar* modest_text_utils_convert_to_html_body (const gchar *data); + /** * modest_text_utils_strftime: diff --git a/src/modest-tny-msg.c b/src/modest-tny-msg.c index c9364af..ac45472 100644 --- a/src/modest-tny-msg.c +++ b/src/modest-tny-msg.c @@ -259,6 +259,11 @@ modest_tny_msg_get_body (TnyMsg *msg, gboolean want_html) gtk_text_buffer_get_bounds (buf, &start, &end); to_quote = gtk_text_buffer_get_text (buf, &start, &end, FALSE); + if (tny_mime_part_content_type_is (body, "text/plain")) { + gchar *to_quote_converted = modest_text_utils_convert_to_html (to_quote); + g_free (to_quote); + to_quote = to_quote_converted; + } g_object_unref (buf); return to_quote; @@ -341,13 +346,14 @@ modest_tny_msg_find_body_part (TnyMsg *msg, gboolean want_html) static TnyMsg * -create_reply_forward_mail (TnyMsg *msg, const gchar *from, gboolean is_reply, guint type) +create_reply_forward_mail (TnyMsg *msg, const gchar *from, const gchar *signature, gboolean is_reply, guint type) { TnyMsg *new_msg; TnyHeader *new_header, *header; gchar *new_subject; TnyMimePart *body; ModestFormatter *formatter; + gchar *subject_prefix; /* Get body from original msg. Always look for the text/plain part of the message to create the reply/forwarded mail */ @@ -355,7 +361,10 @@ create_reply_forward_mail (TnyMsg *msg, const gchar *from, gboolean is_reply, gu body = modest_tny_msg_find_body_part (msg, FALSE); /* TODO: select the formatter from account prefs */ - formatter = modest_formatter_new ("text/plain"); + if (modest_conf_get_bool (modest_runtime_get_conf (), MODEST_CONF_PREFER_FORMATTED_TEXT, NULL)) + formatter = modest_formatter_new ("text/html", signature); + else + formatter = modest_formatter_new ("text/plain", signature); /* Format message body */ if (is_reply) { @@ -388,9 +397,14 @@ create_reply_forward_mail (TnyMsg *msg, const gchar *from, gboolean is_reply, gu tny_header_set_replyto (new_header, from); /* Change the subject */ + if (is_reply) + subject_prefix = g_strconcat (_("mail_va_re"), ":", NULL); + else + subject_prefix = g_strconcat (_("mail_va_fw"), ":", NULL); new_subject = (gchar *) modest_text_utils_derived_subject (tny_header_get_subject(header), - (is_reply) ? _("Re:") : _("Fwd:")); + subject_prefix); + g_free (subject_prefix); tny_header_set_subject (new_header, (const gchar *) new_subject); g_free (new_subject); @@ -417,13 +431,14 @@ add_if_attachment (gpointer data, gpointer user_data) TnyMsg* modest_tny_msg_create_forward_msg (TnyMsg *msg, const gchar *from, + const gchar *signature, ModestTnyMsgForwardType forward_type) { TnyMsg *new_msg; TnyList *parts = NULL; GList *attachments_list = NULL; - new_msg = create_reply_forward_mail (msg, from, FALSE, forward_type); + new_msg = create_reply_forward_mail (msg, from, signature, FALSE, forward_type); /* Add attachments */ parts = TNY_LIST (tny_simple_list_new()); @@ -442,6 +457,7 @@ modest_tny_msg_create_forward_msg (TnyMsg *msg, TnyMsg* modest_tny_msg_create_reply_msg (TnyMsg *msg, const gchar *from, + const gchar *signature, ModestTnyMsgReplyType reply_type, ModestTnyMsgReplyMode reply_mode) { @@ -452,7 +468,7 @@ modest_tny_msg_create_reply_msg (TnyMsg *msg, const gchar *cc = NULL, *bcc = NULL; GString *tmp = NULL; - new_msg = create_reply_forward_mail (msg, from, TRUE, reply_type); + new_msg = create_reply_forward_mail (msg, from, signature, TRUE, reply_type); /* Fill the header */ header = tny_msg_get_header (msg); diff --git a/src/modest-tny-msg.h b/src/modest-tny-msg.h index d443a53..5166b88 100644 --- a/src/modest-tny-msg.h +++ b/src/modest-tny-msg.h @@ -130,6 +130,7 @@ gchar* modest_tny_msg_get_body (TnyMsg *self, gboolean want_html); * modest_tny_msg_create_forward_msg: * @msg: a valid #TnyMsg instance * @from: the sender of the forwarded mail + * @signature: signature to attach to the reply * @forward_type: the type of formatting used to create the forwarded message * * Creates a forwarded message from an existing one @@ -138,12 +139,14 @@ gchar* modest_tny_msg_get_body (TnyMsg *self, gboolean want_html); **/ TnyMsg* modest_tny_msg_create_forward_msg (TnyMsg *msg, const gchar *from, + const gchar *signature, ModestTnyMsgForwardType forward_type); /** * modest_tny_msg_create_reply_msg: * @msg: a valid #TnyMsg instance * @from: the sender of the forwarded mail + * @signature: signature to add to the reply message * @reply_type: the type of formatting used to create the reply message * @reply_mode: the mode of reply: to the sender only, to a mail list or to all * @@ -153,6 +156,7 @@ TnyMsg* modest_tny_msg_create_forward_msg (TnyMsg *msg, **/ TnyMsg* modest_tny_msg_create_reply_msg (TnyMsg *msg, const gchar *from, + const gchar *signature, ModestTnyMsgReplyType reply_type, ModestTnyMsgReplyMode reply_mode); diff --git a/src/modest-ui-actions.c b/src/modest-ui-actions.c index 834beeb..e80862d 100644 --- a/src/modest-ui-actions.c +++ b/src/modest-ui-actions.c @@ -155,6 +155,12 @@ modest_ui_actions_on_delete (GtkAction *action, ModestWindow *win) TnyIterator *iter; g_return_if_fail (MODEST_IS_WINDOW(win)); + + if (MODEST_IS_MSG_EDIT_WINDOW (win)) { + gboolean ret_value; + g_signal_emit_by_name (G_OBJECT (win), "delete-event", NULL, &ret_value); + return; + } header_list = get_selected_headers (win); @@ -204,6 +210,9 @@ modest_ui_actions_on_close_window (GtkAction *action, ModestWindow *win) { if (MODEST_IS_MSG_VIEW_WINDOW (win)) { gtk_widget_destroy (GTK_WIDGET (win)); + } else if (MODEST_IS_MSG_EDIT_WINDOW (win)) { + gboolean ret_value; + g_signal_emit_by_name (G_OBJECT (win), "delete-event", NULL, &ret_value); } else if (MODEST_IS_WINDOW (win)) { gtk_widget_destroy (GTK_WIDGET (win)); } else { @@ -285,9 +294,10 @@ modest_ui_actions_on_new_msg (GtkAction *action, ModestWindow *win) TnyFolder *folder = NULL; gchar *account_name = NULL; gchar *from_str = NULL; - GError *err = NULL; +/* GError *err = NULL; */ TnyAccount *account = NULL; ModestWindowMgr *mgr; + gchar *signature = NULL; account_name = g_strdup(modest_window_get_active_account (win)); if (!account_name) @@ -311,7 +321,15 @@ modest_ui_actions_on_new_msg (GtkAction *action, ModestWindow *win) goto cleanup; } - msg = modest_tny_msg_new ("", from_str, "", "", "", "", NULL); + if (modest_account_mgr_get_bool (modest_runtime_get_account_mgr (), account_name, + MODEST_ACCOUNT_USE_SIGNATURE, FALSE)) { + signature = modest_account_mgr_get_string (modest_runtime_get_account_mgr (), account_name, + MODEST_ACCOUNT_SIGNATURE, FALSE); + } else { + signature = g_strdup (""); + } + + msg = modest_tny_msg_new ("", from_str, "", "", "", signature, NULL); if (!msg) { g_printerr ("modest: failed to create new msg\n"); goto cleanup; @@ -323,13 +341,13 @@ modest_ui_actions_on_new_msg (GtkAction *action, ModestWindow *win) goto cleanup; } - tny_folder_add_msg (folder, msg, &err); - if (err) { - g_printerr ("modest: error adding msg to Drafts folder: %s", - err->message); - g_error_free (err); - goto cleanup; - } +/* tny_folder_add_msg (folder, msg, &err); */ +/* if (err) { */ +/* g_printerr ("modest: error adding msg to Drafts folder: %s", */ +/* err->message); */ +/* g_error_free (err); */ +/* goto cleanup; */ +/* } */ /* Create and register edit window */ msg_win = modest_msg_edit_window_new (msg, account_name); @@ -344,6 +362,7 @@ modest_ui_actions_on_new_msg (GtkAction *action, ModestWindow *win) cleanup: g_free (account_name); g_free (from_str); + g_free (signature); if (account) g_object_unref (G_OBJECT(account)); if (msg) @@ -374,6 +393,7 @@ reply_forward_func (gpointer data, gpointer user_data) TnyFolder *folder = NULL; TnyAccount *account = NULL; ModestWindowMgr *mgr; + gchar *signature = NULL; msg = TNY_MSG (data); helper = (GetMsgAsyncHelper *) user_data; @@ -381,23 +401,30 @@ reply_forward_func (gpointer data, gpointer user_data) from = modest_account_mgr_get_from_string (modest_runtime_get_account_mgr(), rf_helper->account_name); + if (modest_account_mgr_get_bool (modest_runtime_get_account_mgr(), + rf_helper->account_name, + MODEST_ACCOUNT_USE_SIGNATURE, FALSE)) { + signature = modest_account_mgr_get_string (modest_runtime_get_account_mgr (), + rf_helper->account_name, + MODEST_ACCOUNT_SIGNATURE, FALSE); + } /* Create reply mail */ switch (rf_helper->action) { case ACTION_REPLY: new_msg = - modest_tny_msg_create_reply_msg (msg, from, + modest_tny_msg_create_reply_msg (msg, from, signature, rf_helper->reply_forward_type, MODEST_TNY_MSG_REPLY_MODE_SENDER); break; case ACTION_REPLY_TO_ALL: new_msg = - modest_tny_msg_create_reply_msg (msg, from, rf_helper->reply_forward_type, + modest_tny_msg_create_reply_msg (msg, from, signature, rf_helper->reply_forward_type, MODEST_TNY_MSG_REPLY_MODE_ALL); edit_type = MODEST_EDIT_TYPE_REPLY; break; case ACTION_FORWARD: new_msg = - modest_tny_msg_create_forward_msg (msg, from, rf_helper->reply_forward_type); + modest_tny_msg_create_forward_msg (msg, from, signature, rf_helper->reply_forward_type); edit_type = MODEST_EDIT_TYPE_FORWARD; break; default: @@ -405,6 +432,8 @@ reply_forward_func (gpointer data, gpointer user_data) return; } + g_free (signature); + if (!new_msg) { g_printerr ("modest: failed to create message\n"); goto cleanup; @@ -1068,6 +1097,67 @@ modest_ui_actions_on_msg_recpt_activated (ModestMsgView *msgview, } void +modest_ui_actions_on_save_to_drafts (GtkWidget *widget, ModestMsgEditWindow *edit_window) +{ + TnyTransportAccount *transport_account; + ModestMailOperation *mail_operation; + MsgData *data; + gchar *account_name, *from; + ModestAccountMgr *account_mgr; + + g_return_if_fail (MODEST_IS_MSG_EDIT_WINDOW(edit_window)); + + data = modest_msg_edit_window_get_msg_data (edit_window); + + account_mgr = modest_runtime_get_account_mgr(); + account_name = g_strdup(modest_window_get_active_account (MODEST_WINDOW(edit_window))); + if (!account_name) + account_name = modest_account_mgr_get_default_account (account_mgr); + if (!account_name) { + g_printerr ("modest: no account found\n"); + modest_msg_edit_window_free_msg_data (edit_window, data); + return; + } + transport_account = + TNY_TRANSPORT_ACCOUNT(modest_tny_account_store_get_tny_account_by_account + (modest_runtime_get_account_store(), + account_name, + TNY_ACCOUNT_TYPE_TRANSPORT)); + if (!transport_account) { + g_printerr ("modest: no transport account found for '%s'\n", account_name); + g_free (account_name); + modest_msg_edit_window_free_msg_data (edit_window, data); + return; + } + from = modest_account_mgr_get_from_string (account_mgr, account_name); + + /* Create the mail operation */ + mail_operation = modest_mail_operation_new (MODEST_MAIL_OPERATION_ID_INFO); + modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (), mail_operation); + + modest_mail_operation_save_to_drafts (mail_operation, + transport_account, + from, + data->to, + data->cc, + data->bcc, + data->subject, + data->plain_body, + data->html_body, + data->attachments, + data->priority_flags); + /* Frees */ + g_free (from); + g_free (account_name); + g_object_unref (G_OBJECT (transport_account)); + g_object_unref (G_OBJECT (mail_operation)); + + modest_msg_edit_window_free_msg_data (edit_window, data); + + /* Save settings and close the window */ + gtk_widget_destroy (GTK_WIDGET (edit_window)); +} +void modest_ui_actions_on_send (GtkWidget *widget, ModestMsgEditWindow *edit_window) { TnyTransportAccount *transport_account; diff --git a/src/modest-ui-actions.h b/src/modest-ui-actions.h index dea3449..592fc50 100644 --- a/src/modest-ui-actions.h +++ b/src/modest-ui-actions.h @@ -134,6 +134,9 @@ void modest_ui_actions_on_msg_recpt_activated (ModestMsgView *msgview, con void modest_ui_actions_on_send (GtkWidget *widget, ModestMsgEditWindow *edit_window); +void modest_ui_actions_on_save_to_drafts (GtkWidget *widget, + ModestMsgEditWindow *edit_window); + void modest_ui_actions_on_toggle_bold (GtkToggleAction *action, ModestMsgEditWindow *window); diff --git a/src/widgets/modest-msg-edit-window-ui.h b/src/widgets/modest-msg-edit-window-ui.h index eeaa3d0..c216ef8 100644 --- a/src/widgets/modest-msg-edit-window-ui.h +++ b/src/widgets/modest-msg-edit-window-ui.h @@ -51,8 +51,12 @@ static const GtkActionEntry modest_msg_edit_action_entries [] = { { "FileFormat", NULL, N_("mcen_me_editor_file_format") }, { "Tools", NULL, N_("mcen_me_inbox_tools") }, { "ShowToolbar", NULL, N_("mcen_me_inbox_toolbar") }, + { "Close", NULL, N_("mcen_me_inbox_close") }, /* ACTIONS */ + { "ActionsNewMessage", NULL, N_("mcen_me_viewer_newemail"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_new_msg) }, + { "ActionsSaveToDrafts", NULL, N_("mcen_me_editor_save_as_draft"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_save_to_drafts) }, + { "ActionsDelete", NULL, N_("mcen_me_inbox_delete"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_delete) }, { "ActionsSend", NULL, N_("mcen_me_editor_send"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_send) }, /* { "ActionsFontColor", GTK_STOCK_SELECT_COLOR, N_("Color"), NULL, N_("Change text color"), G_CALLBACK (modest_ui_actions_on_select_editor_color)}, */ /* { "BackgroundColor", GTK_STOCK_SELECT_COLOR, N_("Background color"), NULL, N_("Change background color"), G_CALLBACK (modest_ui_actions_on_select_editor_background_color)}, */ @@ -64,6 +68,8 @@ static const GtkActionEntry modest_msg_edit_action_entries [] = { { "SelectAll", NULL, N_("mcen_me_viewer_selectall"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_select_all)}, { "SelectFont", NULL, N_("mcen_me_editor_font"), NULL, NULL, G_CALLBACK (modest_ui_actions_msg_edit_on_select_font)}, { "SelectContacts", NULL, N_("mcen_me_editor_selectrecipients"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_select_contacts)}, + { "CloseWindow", NULL, N_("mcen_me_inbox_close_window"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_close_window)}, + { "CloseAllWindows", NULL, N_("mcen_me_inbox_close_windows"), NULL, NULL, G_CALLBACK (modest_ui_actions_on_quit) }, /* KEY ACCELERATOR ACTIONS */ { "ZoomPlus", NULL, N_("Zoom +"), "F7", NULL, G_CALLBACK (modest_ui_actions_on_zoom_plus) }, diff --git a/src/widgets/modest-recpt-editor.c b/src/widgets/modest-recpt-editor.c index 78a73ab..73be435 100644 --- a/src/widgets/modest-recpt-editor.c +++ b/src/widgets/modest-recpt-editor.c @@ -41,6 +41,7 @@ #include #include #include +#include static GObjectClass *parent_class = NULL; @@ -60,6 +61,7 @@ struct _ModestRecptEditorPrivate GtkWidget *abook_button; GtkWidget *scrolled_window; gchar *recipients; + }; #define MODEST_RECPT_EDITOR_GET_PRIVATE(o) \ @@ -75,6 +77,28 @@ static void modest_recpt_editor_class_init (ModestRecptEditorClass *klass); /* widget events */ static void modest_recpt_editor_on_abook_clicked (GtkButton *button, ModestRecptEditor *editor); +static gboolean modest_recpt_editor_on_button_release_event (GtkWidget *widget, + GdkEventButton *event, + ModestRecptEditor *editor); +static void modest_recpt_editor_move_cursor_to_end (ModestRecptEditor *editor); +static void modest_recpt_editor_on_focus_in (GtkTextView *text_view, + GdkEventFocus *event, + ModestRecptEditor *editor); +static void modest_recpt_editor_on_insert_text (GtkTextBuffer *buffer, + GtkTextIter *location, + gchar *text, + gint len, + ModestRecptEditor *editor); +static gboolean modest_recpt_editor_on_key_press_event (GtkTextView *text_view, + GdkEventKey *key, + ModestRecptEditor *editor); +static GtkTextTag *iter_has_recipient (GtkTextIter *iter); +static gunichar iter_previous_char (GtkTextIter *iter); +/* static gunichar iter_next_char (GtkTextIter *iter); */ +static GtkTextTag *prev_iter_has_recipient (GtkTextIter *iter); +/* static GtkTextTag *next_iter_has_recipient (GtkTextIter *iter); */ +static void select_tag_of_iter (GtkTextIter *iter, GtkTextTag *tag); + /** * modest_recpt_editor_new: @@ -103,9 +127,11 @@ modest_recpt_editor_set_recipients (ModestRecptEditor *recpt_editor, const gchar buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view)); + g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor); gtk_text_buffer_set_text (buffer, recipients, -1); if (GTK_WIDGET_REALIZED (recpt_editor)) gtk_widget_queue_resize (GTK_WIDGET (recpt_editor)); + g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor); } @@ -133,7 +159,12 @@ modest_recpt_editor_add_recipients (ModestRecptEditor *recpt_editor, const gchar gtk_text_buffer_get_end_iter (buffer, &iter); + g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor); + gtk_text_buffer_insert (buffer, &iter, recipients, -1); + + g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor); + if (GTK_WIDGET_REALIZED (recpt_editor)) gtk_widget_queue_resize (GTK_WIDGET (recpt_editor)); @@ -146,17 +177,29 @@ modest_recpt_editor_add_resolved_recipient (ModestRecptEditor *recpt_editor, GSL GtkTextBuffer *buffer = NULL; GtkTextIter start, end, iter; GtkTextTag *tag = NULL; - gboolean is_first_recipient = TRUE; GSList *node; + gboolean is_first_recipient = TRUE; g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor)); priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor); buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view)); + g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor); gtk_text_buffer_get_bounds (buffer, &start, &end); - if (!gtk_text_iter_equal (&start, &end)) - gtk_text_buffer_insert (buffer, &end, ";\n", -1); + if (gtk_text_buffer_get_char_count (buffer) > 0) { + gchar * buffer_contents; + + gtk_text_buffer_get_bounds (buffer, &start, &end); + buffer_contents = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); + if (!g_str_has_suffix (buffer_contents, "\n")) { + if (g_str_has_suffix (buffer_contents, ";")||(g_str_has_suffix (buffer_contents, ","))) + gtk_text_buffer_insert (buffer, &end, "\n", -1); + else + gtk_text_buffer_insert (buffer, &end, ";\n", -1); + } + g_free (buffer_contents); + } gtk_text_buffer_get_end_iter (buffer, &iter); @@ -169,19 +212,21 @@ modest_recpt_editor_add_resolved_recipient (ModestRecptEditor *recpt_editor, GSL g_object_set_data_full (G_OBJECT (tag), "recipient-id", g_strdup (recipient_id), (GDestroyNotify) g_free); for (node = email_list; node != NULL; node = g_slist_next (node)) { - gchar *recipient = (gchar *) email_list->data; + gchar *recipient = (gchar *) node->data; if ((recipient) && (strlen (recipient) != 0)) { - if (!is_first_recipient) { - gtk_text_buffer_insert (buffer, &iter, ";\n", -1); - } else { - is_first_recipient = FALSE; - } + if (!is_first_recipient) + gtk_text_buffer_insert (buffer, &iter, "\n", -1); gtk_text_buffer_insert_with_tags (buffer, &iter, recipient, -1, tag, NULL); + gtk_text_buffer_insert (buffer, &iter, ";", -1); + is_first_recipient = FALSE; } } + g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, recpt_editor); + + modest_recpt_editor_move_cursor_to_end (recpt_editor); } @@ -222,6 +267,7 @@ modest_recpt_editor_instance_init (GTypeInstance *instance, gpointer g_class) { ModestRecptEditorPrivate *priv; GtkWidget *abook_icon; + GtkTextBuffer *buffer; priv = MODEST_RECPT_EDITOR_GET_PRIVATE (instance); @@ -252,10 +298,14 @@ modest_recpt_editor_instance_init (GTypeInstance *instance, gpointer g_class) gtk_widget_set_size_request (priv->text_view, 75, -1); + buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view)); g_signal_connect (G_OBJECT (priv->abook_button), "clicked", G_CALLBACK (modest_recpt_editor_on_abook_clicked), instance); + g_signal_connect (G_OBJECT (priv->text_view), "button-release-event", G_CALLBACK (modest_recpt_editor_on_button_release_event), instance); + g_signal_connect (G_OBJECT (priv->text_view), "key-press-event", G_CALLBACK (modest_recpt_editor_on_key_press_event), instance); + g_signal_connect (G_OBJECT (priv->text_view), "focus-in-event", G_CALLBACK (modest_recpt_editor_on_focus_in), instance); + g_signal_connect (G_OBJECT (buffer), "insert-text", G_CALLBACK (modest_recpt_editor_on_insert_text), instance); - GTK_WIDGET_SET_FLAGS (GTK_WIDGET (instance), GTK_CAN_FOCUS); - gtk_container_set_focus_child (GTK_CONTAINER (instance), priv->text_view); +/* gtk_container_set_focus_child (GTK_CONTAINER (instance), priv->text_view); */ return; } @@ -272,6 +322,17 @@ modest_recpt_editor_set_field_size_group (ModestRecptEditor *recpt_editor, GtkSi gtk_size_group_add_widget (size_group, priv->scrolled_window); } +GtkTextBuffer * +modest_recpt_editor_get_buffer (ModestRecptEditor *recpt_editor) +{ + ModestRecptEditorPrivate *priv; + + g_return_val_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor), NULL); + priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor); + + return gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view)); +} + static void modest_recpt_editor_on_abook_clicked (GtkButton *button, ModestRecptEditor *editor) { @@ -280,6 +341,341 @@ modest_recpt_editor_on_abook_clicked (GtkButton *button, ModestRecptEditor *edit g_signal_emit_by_name (G_OBJECT (editor), "open-addressbook"); } +static gboolean +modest_recpt_editor_on_button_release_event (GtkWidget *widget, + GdkEventButton *event, + ModestRecptEditor *recpt_editor) +{ + ModestRecptEditorPrivate *priv; + GtkTextIter location, start, end; + GtkTextMark *mark; + GtkTextBuffer *buffer; + GtkTextTag *tag; + gboolean selection_changed = FALSE; + + priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor); + + buffer = modest_recpt_editor_get_buffer (recpt_editor); + mark = gtk_text_buffer_get_insert (buffer); + gtk_text_buffer_get_iter_at_mark (buffer, &location, mark); + g_message ("RELEASE OFFSET %d", gtk_text_iter_get_offset (&location)); + + gtk_text_buffer_get_selection_bounds (buffer, &start, &end); + + tag = iter_has_recipient (&start); + if (tag != NULL) + if (!gtk_text_iter_begins_tag (&start, tag)) { + gtk_text_iter_backward_to_tag_toggle (&start, tag); + selection_changed = TRUE; + } + + tag = iter_has_recipient (&end); + if (tag != NULL) + if (!gtk_text_iter_ends_tag (&end, tag)) { + gtk_text_iter_forward_to_tag_toggle (&end, tag); + selection_changed = TRUE; + } + + gtk_text_buffer_select_range (buffer, &start, &end); + + return FALSE; +} + +static void +modest_recpt_editor_on_focus_in (GtkTextView *text_view, + GdkEventFocus *event, + ModestRecptEditor *editor) +{ + ModestRecptEditorPrivate *priv = MODEST_RECPT_EDITOR_GET_PRIVATE (editor); + modest_recpt_editor_move_cursor_to_end (editor); + gtk_text_view_place_cursor_onscreen (GTK_TEXT_VIEW (priv->text_view)); +} + +static void +modest_recpt_editor_on_insert_text (GtkTextBuffer *buffer, + GtkTextIter *location, + gchar *text, + gint len, + ModestRecptEditor *editor) +{ + GtkTextIter prev; + gunichar prev_char; + ModestRecptEditorPrivate *priv = MODEST_RECPT_EDITOR_GET_PRIVATE (editor); + + if (iter_has_recipient (location)) { + gtk_text_buffer_get_end_iter (buffer, location); + gtk_text_buffer_place_cursor (buffer, location); + } + + if (gtk_text_iter_is_start (location)) + return; + + if (gtk_text_iter_is_end (location)) { + prev = *location; + if (!gtk_text_iter_backward_char (&prev)) + return; + prev_char = gtk_text_iter_get_char (&prev); + g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, editor); + if (prev_char == ';'||prev_char == ',') { + GtkTextMark *insert; + gtk_text_buffer_insert (buffer, location, "\n",-1); + insert = gtk_text_buffer_get_insert (buffer); + gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (priv->text_view), location, 0.0,TRUE, 0.0, 1.0); + } + g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, editor); + + } + +} + +static GtkTextTag * +iter_has_recipient (GtkTextIter *iter) +{ + GSList *tags, *node; + GtkTextTag *result = NULL; + + tags = gtk_text_iter_get_tags (iter); + + for (node = tags; node != NULL; node = g_slist_next (node)) { + GtkTextTag *tag = GTK_TEXT_TAG (node->data); + + if (g_object_get_data (G_OBJECT (tag), "recipient-tag-id")) { + result = tag; + break; + } + } + g_slist_free (tags); + return result; +} + +static GtkTextTag * +prev_iter_has_recipient (GtkTextIter *iter) +{ + GtkTextIter prev; + + prev = *iter; + gtk_text_iter_backward_char (&prev); + return iter_has_recipient (&prev); +} + +/* static GtkTextTag * */ +/* next_iter_has_recipient (GtkTextIter *iter) */ +/* { */ +/* GtkTextIter next; */ + +/* next = *iter; */ +/* return iter_has_recipient (&next); */ +/* } */ + +static gunichar +iter_previous_char (GtkTextIter *iter) +{ + GtkTextIter prev; + + prev = *iter; + gtk_text_iter_backward_char (&prev); + return gtk_text_iter_get_char (&prev); +} + +/* static gunichar */ +/* iter_next_char (GtkTextIter *iter) */ +/* { */ +/* GtkTextIter next; */ + +/* next = *iter; */ +/* gtk_text_iter_forward_char (&next); */ +/* return gtk_text_iter_get_char (&next); */ +/* } */ + +static void +select_tag_of_iter (GtkTextIter *iter, GtkTextTag *tag) +{ + GtkTextIter start, end; + + start = *iter; + if (!gtk_text_iter_begins_tag (&start, tag)) + gtk_text_iter_backward_to_tag_toggle (&start, tag); + end = *iter; + if (!gtk_text_iter_ends_tag (&end, tag)) + gtk_text_iter_forward_to_tag_toggle (&end, tag); + gtk_text_buffer_select_range (gtk_text_iter_get_buffer (iter), &start, &end); +} + +static gboolean +modest_recpt_editor_on_key_press_event (GtkTextView *text_view, + GdkEventKey *key, + ModestRecptEditor *editor) +{ + GtkTextMark *insert; + GtkTextBuffer * buffer; + GtkTextIter location; + GtkTextTag *tag; + + buffer = gtk_text_view_get_buffer (text_view); + insert = gtk_text_buffer_get_insert (buffer); + + /* cases to cover: + * * cursor is on resolved recipient: + * - right should go to first character after the recipient (usually ; or ,) + * - left should fo to the first character before the recipient + * - return should run check names on the recipient. + * * cursor is just after a recipient: + * - right should go to the next character. If it's a recipient, should select + * it + * - left should go to the previous character. If it's a recipient, should go + * to the first character of the recipient, and select it. + * * cursor is on arbitrary text: + * - return should add a ; and go to the next line + * - left or right standard ones. + * * cursor is after a \n: + * - left should go to the character before the \n (as if \n was not a character) + * * cursor is before a \n: + * - right should go to the character after the \n + */ + + gtk_text_buffer_get_iter_at_mark (buffer, &location, insert); + + switch (key->keyval) { + case GDK_Left: + case GDK_KP_Left: + { + gboolean cursor_ready = FALSE; + while (!cursor_ready) { + if (iter_previous_char (&location) == '\n') { + g_message ("INTRO FOUND"); + gtk_text_iter_backward_char (&location); + } else { + cursor_ready = TRUE; + } + } + tag = iter_has_recipient (&location); + if ((tag != NULL)&& (gtk_text_iter_is_start (&location) || !(gtk_text_iter_begins_tag (&location, tag)))) { + select_tag_of_iter (&location, tag); + gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), insert, 0.0, FALSE, 0.0, 1.0); + return TRUE; + } + gtk_text_iter_backward_char (&location); + tag = iter_has_recipient (&location); + if (tag != NULL) + select_tag_of_iter (&location, tag); + else { + gtk_text_buffer_place_cursor (buffer, &location); + } + gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), insert, 0.0, FALSE, 0.0, 1.0); + + return TRUE; + } + break; + case GDK_Right: + case GDK_KP_Right: + { + gboolean cursor_moved = FALSE; + + tag = iter_has_recipient (&location); + if ((tag != NULL)&&(!gtk_text_iter_ends_tag (&location, tag))) { + gtk_text_iter_forward_to_tag_toggle (&location, tag); + while (gtk_text_iter_get_char (&location) == '\n') + gtk_text_iter_forward_char (&location); + gtk_text_buffer_place_cursor (buffer, &location); + gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), insert, 0.0, FALSE, 0.0, 1.0); + return TRUE; + } + + while (gtk_text_iter_get_char (&location) == '\n') { + gtk_text_iter_forward_char (&location); + cursor_moved = TRUE; + } + if (!cursor_moved) + gtk_text_iter_forward_char (&location); + while (gtk_text_iter_get_char (&location) == '\n') { + gtk_text_iter_forward_char (&location); + cursor_moved = TRUE; + } + + tag = iter_has_recipient (&location); + if (tag != NULL) + select_tag_of_iter (&location, tag); + else + gtk_text_buffer_place_cursor (buffer, &location); + gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view), insert, 0.0, FALSE, 0.0, 1.0); + return TRUE; + } + break; + case GDK_Return: + case GDK_KP_Enter: + { + g_signal_handlers_block_by_func (buffer, modest_recpt_editor_on_insert_text, editor); + tag = iter_has_recipient (&location); + if (tag != NULL) { + gtk_text_buffer_get_end_iter (buffer, &location); + gtk_text_buffer_place_cursor (buffer, &location); + if ((iter_previous_char (&location) != ';')&&(iter_previous_char (&location) != ',')) + gtk_text_buffer_insert_at_cursor (buffer, ";", -1); + gtk_text_buffer_insert_at_cursor (buffer, "\n", -1); + } else { + gunichar prev_char = iter_previous_char (&location); + if ((gtk_text_iter_is_start (&location))||(prev_char == '\n') + ||(prev_char == ';')||(prev_char == ',')) + g_signal_emit_by_name (G_OBJECT (editor), "open-addressbook"); + else { + if ((prev_char != ';') && (prev_char != ',')) + gtk_text_buffer_insert_at_cursor (buffer, ";", -1); + gtk_text_buffer_insert_at_cursor (buffer, "\n", -1); + } + } + g_signal_handlers_unblock_by_func (buffer, modest_recpt_editor_on_insert_text, editor); + return TRUE; + } + break; + case GDK_BackSpace: + { + if (gtk_text_buffer_get_has_selection (buffer)) { + gtk_text_buffer_delete_selection (buffer, TRUE, TRUE); + return TRUE; + } + tag = prev_iter_has_recipient (&location); + if (tag != NULL) { + GtkTextIter iter_in_tag; + iter_in_tag = location; + g_message ("DELETE PREV SELECTION"); + gtk_text_iter_backward_char (&iter_in_tag); + select_tag_of_iter (&iter_in_tag, tag); + gtk_text_buffer_delete_selection (buffer, TRUE, TRUE); + return TRUE; + } + return FALSE; + } + break; + default: + return FALSE; + } +} + +static void +modest_recpt_editor_move_cursor_to_end (ModestRecptEditor *editor) +{ + ModestRecptEditorPrivate *priv = MODEST_RECPT_EDITOR_GET_PRIVATE (editor); + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view)); + GtkTextIter start, end; + + gtk_text_buffer_get_end_iter (buffer, &start); + end = start; + gtk_text_buffer_select_range (buffer, &start, &end); + + +} + +void +modest_recpt_editor_grab_focus (ModestRecptEditor *recpt_editor) +{ + ModestRecptEditorPrivate *priv; + + g_return_if_fail (MODEST_IS_RECPT_EDITOR (recpt_editor)); + priv = MODEST_RECPT_EDITOR_GET_PRIVATE (recpt_editor); + + gtk_widget_grab_focus (priv->text_view); +} + static void modest_recpt_editor_finalize (GObject *object) { diff --git a/src/widgets/modest-recpt-editor.h b/src/widgets/modest-recpt-editor.h index 7ef9d04..f84dae8 100644 --- a/src/widgets/modest-recpt-editor.h +++ b/src/widgets/modest-recpt-editor.h @@ -32,6 +32,7 @@ #include #include #include +#include G_BEGIN_DECLS @@ -70,6 +71,8 @@ void modest_recpt_editor_add_recipients (ModestRecptEditor *recpt_editor, const void modest_recpt_editor_add_resolved_recipient (ModestRecptEditor *recpt_editor, GSList *email_list, const gchar * recipient_id); void modest_recpt_editor_set_field_size_group (ModestRecptEditor *recpt_editor, GtkSizeGroup *size_group); +GtkTextBuffer *modest_recpt_editor_get_buffer (ModestRecptEditor *recpt_editor); +void modest_recpt_editor_grab_focus (ModestRecptEditor *recpt_editor); G_END_DECLS diff --git a/src/widgets/modest-recpt-view.c b/src/widgets/modest-recpt-view.c index 88ec9a7..88d68e8 100644 --- a/src/widgets/modest-recpt-view.c +++ b/src/widgets/modest-recpt-view.c @@ -195,7 +195,7 @@ modest_recpt_view_instance_init (GTypeInstance *instance, gpointer g_class) text_view = GTK_TEXT_VIEW(modest_scroll_text_get_text_view (MODEST_SCROLL_TEXT (instance))); g_signal_connect (G_OBJECT (text_view), "button-press-event", G_CALLBACK (button_press_event), instance); - g_signal_connect (G_OBJECT (text_view), "button-release-event", G_CALLBACK (button_release_event), instance); + g_signal_connect_after (G_OBJECT (text_view), "button-release-event", G_CALLBACK (button_release_event), instance); return; } diff --git a/src/widgets/modest-scroll-text.c b/src/widgets/modest-scroll-text.c index 25f6426..a2b2c93 100644 --- a/src/widgets/modest-scroll-text.c +++ b/src/widgets/modest-scroll-text.c @@ -97,14 +97,12 @@ size_request (GtkWidget *widget, /* Count lines in text view */ for (line = 0; line < line_limit; line++) { - if (!gtk_text_view_forward_display_line (GTK_TEXT_VIEW (text_view), &iter)) + if (!gtk_text_view_forward_display_line_end (GTK_TEXT_VIEW (text_view), &iter)) break; + else + gtk_text_view_forward_display_line (GTK_TEXT_VIEW (text_view), &iter); } - /* Put again the cursor in the first character. Also scroll to first line */ - gtk_text_buffer_place_cursor (buffer, &insert_iter); - gtk_text_view_place_cursor_onscreen (GTK_TEXT_VIEW (text_view)); - /* Change the adjustment properties for one line per step behavior */ adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (widget)); if (adj != NULL) { @@ -125,6 +123,10 @@ size_request (GtkWidget *widget, priv->line_height = iter_rectangle.height; + /* Put again the cursor in the first character. Also scroll to first line */ + gtk_text_buffer_place_cursor (buffer, &insert_iter); + gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (text_view), insert_mark); + } static void -- 1.7.9.5
%s%s
%s%s
%s%s