* src/modest-ui-actions.[ch]:
authorJose Dapena Paz <jdapena@igalia.com>
Mon, 30 Apr 2007 11:48:32 +0000 (11:48 +0000)
committerJose Dapena Paz <jdapena@igalia.com>
Mon, 30 Apr 2007 11:48:32 +0000 (11:48 +0000)
        * (..._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

23 files changed:
src/maemo/easysetup/modest-easysetup-wizard.h
src/maemo/easysetup/modest-wizard-dialog.c
src/maemo/modest-address-book.c
src/maemo/modest-main-window-ui.h
src/maemo/modest-msg-edit-window.c
src/maemo/ui/modest-msg-edit-window-ui.xml
src/maemo/ui/modest-msg-view-window-ui.xml
src/modest-defs.h
src/modest-formatter.c
src/modest-formatter.h
src/modest-mail-operation.c
src/modest-mail-operation.h
src/modest-text-utils.c
src/modest-text-utils.h
src/modest-tny-msg.c
src/modest-tny-msg.h
src/modest-ui-actions.c
src/modest-ui-actions.h
src/widgets/modest-msg-edit-window-ui.h
src/widgets/modest-recpt-editor.c
src/widgets/modest-recpt-editor.h
src/widgets/modest-recpt-view.c
src/widgets/modest-scroll-text.c

index 00e6df1..9c3cb80 100644 (file)
@@ -9,6 +9,7 @@
 /* #include <hildon-widgets/hildon-wizard-dialog.h> */
 #include "modest-wizard-dialog.h" /* We use a copied-and-improved HildonWizardDialog. */
 #include "modest-account-mgr.h"
+#include <config.h>
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
index f856c64..2131caa 100644 (file)
@@ -21,6 +21,7 @@
  * widget provided by users contains the actual wizard pages.
  */
 
+#include <config.h>
 #include <gtk/gtkdialog.h>
 #include <gtk/gtknotebook.h>
 #include <gtk/gtkimage.h>
@@ -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;
index b28a51d..183034a 100644 (file)
@@ -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));
index a3d27c6..69841f5 100644 (file)
@@ -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"),      "<CTRL>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"),          "<CTRL>O", NULL,   NULL },
        { "EmailReply",       NULL,  N_("mcen_me_inbox_reply"),            NULL,      NULL,   G_CALLBACK (modest_ui_actions_on_reply) },
index fe64bf1..da30d63 100644 (file)
@@ -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 <tny-simple-list.h>
 #include <wptextview.h>
 #include <wptextbuffer.h>
@@ -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;
+}
+
index 94b93d0..a156210 100644 (file)
   <menubar name="MenuBar">
 
     <menu name="EmailMenu" action="Email">
+      <menuitem name="SendMenu" action="ActionsSend"/>
+      <menuitem name="SaveToDraftsMenu" action="ActionsSaveToDrafts"/>
+      <separator/>
       <menuitem name="SelectContactsMenu" action="SelectContacts"/>
+      <separator/>
+      <menuitem name="NewMessageMenu" action="ActionsNewMessage"/>
+      <separator/>
+      <menuitem name="DeleteMenu" action="ActionsDelete"/>
     </menu>
 
     <menu name="EditMenu" action="Edit">
       </menu>
     </menu>
 
+    <menu name="CloseMenu" action="Close">
+      <menuitem name="CloseWindowMenu" action="CloseWindow"/>
+      <menuitem name="CloseAllWindowsMenu" action="CloseAllWindows"/>
+    </menu>
+
   </menubar>
 
   <toolbar name="ToolBar">
index 6c8531e..86517fe 100644 (file)
@@ -37,7 +37,7 @@
       <menuitem name="MessageReplyAllMenu" action="EmailReplyAll"/>
       <menuitem name="MessageForwardMenu" action="EmailForward"/>
       <separator/>
-      <menuitem name="MessageNewMenu"  action="EmailNew"/>
+      <menuitem name="MessageNewMenu"  action="EmailNewDefault"/>
       <separator/>
       <menuitem name="MessageDeleteMenu" action="EmailDelete"/>
       <separator/>
index 4d2a1a0..f0da7d9 100644 (file)
@@ -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.
index ff73f89..8fcbf0d 100644 (file)
@@ -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);
index cf5c040..b8c5a92 100644 (file)
@@ -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:
index a60c025..2d24e88 100644 (file)
@@ -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)
 {
index e7e4081..d2739b9 100644 (file)
@@ -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
index 243ede3..60ee401 100644 (file)
@@ -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<br>\n<table width=\"100%\" border=\"0\" cellspacing=\"2\" cellpadding=\"2\">\n" \
+               "%s%s<br>\n<table width=\"100%\" border=\"0\" cellspacing=\"2\" cellpadding=\"2\">\n" \
                "<tr><td>%s</td><td>%s</td></tr>\n" \
                "<tr><td>%s</td><td>%s</td></tr>\n" \
                "<tr><td>%s</td><td>%s</td></tr>\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, "<br/>", 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,
-                               "<html><head>"
-                               "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf8\">"
-                               "</head>"
-                               "<body><tt>");
-       
        /* 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, "</tt></body></html>");
+       if (!data)
+               return NULL;
+
+       len = strlen (data);
+       html = g_string_sized_new (1.5 * len);  /* just a  guess... */
+
+       g_string_append_printf (html,
+                               "<html><head>"
+                               "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf8\">"
+                               "</head>"
+                               "<body>");
+
+       modest_text_utils_convert_buffer_to_html (html, data);
+       
+       g_string_append (html, "</body></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);
index ffe623f..0a047f4 100644 (file)
@@ -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:
index c9364af..ac45472 100644 (file)
@@ -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);
index d443a53..5166b88 100644 (file)
@@ -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);
 
index 834beeb..e80862d 100644 (file)
@@ -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;
index dea3449..592fc50 100644 (file)
@@ -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);
index eeaa3d0..c216ef8 100644 (file)
@@ -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) },
index 78a73ab..73be435 100644 (file)
@@ -41,6 +41,7 @@
 #include <modest-scroll-text.h>
 #include <pango/pango-attributes.h>
 #include <string.h>
+#include <gdk/gdkkeysyms.h>
 
 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)
 {
index 7ef9d04..f84dae8 100644 (file)
@@ -32,6 +32,7 @@
 #include <glib-object.h>
 #include <gtk/gtkhbox.h>
 #include <gtk/gtksizegroup.h>
+#include <gtk/gtktextbuffer.h>
 
 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
 
index 88ec9a7..88d68e8 100644 (file)
@@ -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;
 }
index 25f6426..a2b2c93 100644 (file)
@@ -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