2006-05-18 [colin] 2.2.0cvs33
[claws.git] / src / compose.c
index b58d246ae8e19f7f9d79a8a999fc0042d022b55b..c03a639bb8a6f1c7ea8da5407704ee3c123b49e5 100644 (file)
@@ -646,6 +646,9 @@ static GtkItemFactoryEntry compose_entries[] =
                                        NULL, compose_check_backwards , 0, NULL},
        {N_("/_Spelling/_Forward to next misspelled word"),
                                        NULL, compose_check_forwards_go, 0, NULL},
+       {N_("/_Spelling/---"),          NULL, NULL, 0, "<Separator>"},
+       {N_("/_Spelling/Options"),
+                                       NULL, NULL, 0, "<Branch>"},
 #endif
        {N_("/_Options"),               NULL, NULL, 0, "<Branch>"},
        {N_("/_Options/Privacy System"),                NULL, NULL,   0, "<Branch>"},
@@ -902,7 +905,7 @@ static void compose_create_tags(GtkTextView *text, Compose *compose)
                        GtkStyle *style;
 
                        g_warning("Compose: color allocation failed.\n");
-                       style = gtk_widget_get_style(text);
+                       style = gtk_widget_get_style(GTK_WIDGET(text));
                        quote_color1 = quote_color2 = quote_color3 = 
                                quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = 
                                signature_color = uri_color = black;
@@ -2642,6 +2645,7 @@ static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
        g_return_if_fail(msginfo != NULL);
 
        SET_ENTRY(subject_entry, msginfo->subject);
+       SET_ENTRY(from_name, msginfo->from);
        SET_ADDRESS(COMPOSE_TO, msginfo->to);
        SET_ADDRESS(COMPOSE_CC, compose->cc);
        SET_ADDRESS(COMPOSE_BCC, compose->bcc);
@@ -3769,15 +3773,84 @@ compose_current_mail_account(void)
        return ac;
 }
 
+#define QUOTE_IF_REQUIRED(out, str)                                    \
+{                                                                      \
+       if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
+               gchar *__tmp;                                           \
+               gint len;                                               \
+                                                                       \
+               len = strlen(str) + 3;                                  \
+               if ((__tmp = alloca(len)) == NULL) {                    \
+                       g_warning("can't allocate memory\n");           \
+                       g_string_free(header, TRUE);                    \
+                       return NULL;                                    \
+               }                                                       \
+               g_snprintf(__tmp, len, "\"%s\"", str);                  \
+               out = __tmp;                                            \
+       } else {                                                        \
+               gchar *__tmp;                                           \
+                                                                       \
+               if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
+                       g_warning("can't allocate memory\n");           \
+                       g_string_free(header, TRUE);                    \
+                       return NULL;                                    \
+               } else                                                  \
+                       strcpy(__tmp, str);                             \
+                                                                       \
+               out = __tmp;                                            \
+       }                                                               \
+}
+
+#define QUOTE_IF_REQUIRED_NORMAL(out, str, errret)                     \
+{                                                                      \
+       if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
+               gchar *__tmp;                                           \
+               gint len;                                               \
+                                                                       \
+               len = strlen(str) + 3;                                  \
+               if ((__tmp = alloca(len)) == NULL) {                    \
+                       g_warning("can't allocate memory\n");           \
+                       errret;                                         \
+               }                                                       \
+               g_snprintf(__tmp, len, "\"%s\"", str);                  \
+               out = __tmp;                                            \
+       } else {                                                        \
+               gchar *__tmp;                                           \
+                                                                       \
+               if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
+                       g_warning("can't allocate memory\n");           \
+                       errret;                                         \
+               } else                                                  \
+                       strcpy(__tmp, str);                             \
+                                                                       \
+               out = __tmp;                                            \
+       }                                                               \
+}
+
 static void compose_select_account(Compose *compose, PrefsAccount *account,
                                   gboolean init)
 {
        GtkItemFactory *ifactory;
+       gchar *from = NULL;
 
        g_return_if_fail(account != NULL);
 
        compose->account = account;
 
+       if (account->name && *account->name) {
+               gchar *buf;
+               QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
+               from = g_strdup_printf("%s <%s>",
+                                      buf, account->address);
+               gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
+       } else {
+               from = g_strdup_printf("<%s>",
+                                      account->address);
+               gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
+       }
+
+       g_free(from);
+
        compose_set_title(compose);
 
        ifactory = gtk_item_factory_from_widget(compose->menubar);
@@ -3981,12 +4054,12 @@ gint compose_send(Compose *compose)
        }
 
        if (msgnum == 0) {
-               if (!discard_window) {
-                       goto bail;
-               }
                alertpanel_error(_("The message was queued but could not be "
                                   "sent.\nUse \"Send queued messages\" from "
                                   "the main window to retry."));
+               if (!discard_window) {
+                       goto bail;
+               }
                return -1;
        }
        
@@ -4014,12 +4087,12 @@ gint compose_send(Compose *compose)
                if (!discard_window)
                        compose_close(compose);
        } else {
-               if (!discard_window) {
-                       goto bail;              
-               }
                alertpanel_error(_("The message was queued but could not be "
                                   "sent.\nUse \"Send queued messages\" from "
                                   "the main window to retry."));
+               if (!discard_window) {
+                       goto bail;              
+               }
                return -1;
        }
 
@@ -4634,13 +4707,20 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item,
        if (newsac)
                fprintf(fp, "NAID:%d\n", newsac->account_id);
 
+       
        if (compose->privacy_system != NULL) {
                fprintf(fp, "X-Sylpheed-Privacy-System:%s\n", compose->privacy_system);
                fprintf(fp, "X-Sylpheed-Sign:%d\n", compose->use_signing);
                if (compose->use_encryption) {
                        gchar *encdata;
-
-                       encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
+                       if (mailac && mailac->encrypt_to_self) {
+                               GSList *tmp_list = g_slist_copy(compose->to_list);
+                               tmp_list = g_slist_append(tmp_list, compose->account->address);
+                               encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
+                               g_slist_free(tmp_list);
+                       } else {
+                               encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
+                       }
                        if (encdata != NULL) {
                                if (strcmp(encdata, "_DONT_ENCRYPT_")) {
                                        fprintf(fp, "X-Sylpheed-Encrypt:%d\n", compose->use_encryption);
@@ -4810,34 +4890,6 @@ static void compose_add_attachments(Compose *compose, MimeInfo *parent)
        } while (gtk_tree_model_iter_next(model, &iter));
 }
 
-#define QUOTE_IF_REQUIRED(out, str)                                    \
-{                                                                      \
-       if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
-               gchar *__tmp;                                           \
-               gint len;                                               \
-                                                                       \
-               len = strlen(str) + 3;                                  \
-               if ((__tmp = alloca(len)) == NULL) {                    \
-                       g_warning("can't allocate memory\n");           \
-                       g_string_free(header, TRUE);                    \
-                       return NULL;                                    \
-               }                                                       \
-               g_snprintf(__tmp, len, "\"%s\"", str);                  \
-               out = __tmp;                                            \
-       } else {                                                        \
-               gchar *__tmp;                                           \
-                                                                       \
-               if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
-                       g_warning("can't allocate memory\n");           \
-                       g_string_free(header, TRUE);                    \
-                       return NULL;                                    \
-               } else                                                  \
-                       strcpy(__tmp, str);                             \
-                                                                       \
-               out = __tmp;                                            \
-       }                                                               \
-}
-
 #define IS_IN_CUSTOM_HEADER(header) \
        (compose->account->add_customhdr && \
         custom_header_find(compose->account->customhdr_list, header) != NULL)
@@ -4906,6 +4958,8 @@ static gchar *compose_get_header(Compose *compose)
        GSList *list;
        gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
        GString *header;
+       gchar *from_name = NULL, *from_address = NULL;
+       gchar *tmp;
 
        g_return_val_if_fail(compose->account != NULL, NULL);
        g_return_val_if_fail(compose->account->address != NULL, NULL);
@@ -4919,16 +4973,52 @@ static gchar *compose_get_header(Compose *compose)
        }
 
        /* From */
+       
        if (compose->account->name && *compose->account->name) {
+               gchar *buf;
+               QUOTE_IF_REQUIRED(buf, compose->account->name);
+               tmp = g_strdup_printf("%s <%s>",
+                       buf, compose->account->address);
+       } else {
+               tmp = g_strdup_printf("%s",
+                       compose->account->address);
+       }
+       if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
+       ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
+               /* use default */
+               from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
+               from_address = g_strdup(compose->account->address);
+       } else {
+               gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
+               /* extract name and address */
+               if (strstr(spec, " <") && strstr(spec, ">")) {
+                       from_address = g_strdup(strrchr(spec, '<')+1);
+                       *(strrchr(from_address, '>')) = '\0';
+                       from_name = g_strdup(spec);
+                       *(strrchr(from_name, '<')) = '\0';
+               } else {
+                       from_name = NULL;
+                       from_address = g_strdup(spec);
+               }
+               g_free(spec);
+       }
+       g_free(tmp);
+       
+       
+       if (from_name && *from_name) {
                compose_convert_header
-                       (compose, buf, sizeof(buf), compose->account->name,
+                       (compose, buf, sizeof(buf), from_name,
                         strlen("From: "), TRUE);
                QUOTE_IF_REQUIRED(name, buf);
+               
                g_string_append_printf(header, "From: %s <%s>\n",
-                       name, compose->account->address);
+                       name, from_address);
        } else
-               g_string_append_printf(header, "From: %s\n", compose->account->address);
+               g_string_append_printf(header, "From: %s\n", from_address);
        
+       g_free(from_name);
+       g_free(from_address);
+
        /* To */
        compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
 
@@ -5150,7 +5240,8 @@ static void compose_convert_header(Compose *compose, gchar *dest, gint len, gcha
                        test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
                }
 
-               if (!test_conv_global_out && compose->orig_charset) {
+               if (!test_conv_global_out && compose->orig_charset
+               &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
                        out_codeset = compose->orig_charset;
                        debug_print("failure; trying to convert to %s\n", out_codeset);
                        test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
@@ -5228,6 +5319,8 @@ static void compose_create_header_entry(Compose *compose)
        /* Entry field */
        entry = gtk_entry_new(); 
        gtk_widget_show(entry);
+       gtk_tooltips_set_tip(compose->tooltips, entry,
+               _("Use <tab> to autocomplete from addressbook"), NULL);
        gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2, compose->header_nextrow, compose->header_nextrow+1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
 
         g_signal_connect(G_OBJECT(entry), "key-press-event", 
@@ -5297,11 +5390,11 @@ static GtkWidget *compose_create_header(Compose *compose)
        hbox = gtk_hbox_new(FALSE, 0);
        label = gtk_label_new(prefs_common.trans_hdr ? _("From:") : "From:");
        gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
-       gtk_table_attach(GTK_TABLE(header_table), hbox, 0, 1, count, count + 1,
-                        GTK_FILL, 0, 2, 0);
+       //gtk_table_attach(GTK_TABLE(header_table), hbox, 0, 1, count, count + 1,
+       //               GTK_FILL, 0, 2, 0);
        from_optmenu_hbox = compose_account_option_menu_create(compose);
        gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
-                                 1, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
+                                 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
        count++;
 
        compose->header_table = header_table;
@@ -5554,6 +5647,49 @@ static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
        return FALSE;
 }
 
+#if USE_ASPELL
+static void compose_spell_menu_changed(void *data)
+{
+       Compose *compose = (Compose *)data;
+       GSList *items;
+       GtkWidget *menuitem;
+       GtkWidget *parent_item;
+       GtkMenu *menu = GTK_MENU(gtk_menu_new());
+       GtkItemFactory *ifactory = gtk_item_factory_from_widget(compose->menubar);
+       GSList *spell_menu;
+
+       if (compose->gtkaspell == NULL)
+               return;
+
+       parent_item = gtk_item_factory_get_item(ifactory, 
+                       "/Spelling/Options");
+
+       /* setting the submenu removes /Spelling/Options from the factory 
+        * so we need to save it */
+
+       if (parent_item == NULL) {
+               parent_item = compose->aspell_options_menu;
+               gtk_menu_item_remove_submenu(GTK_MENU_ITEM(parent_item));
+       } else
+               compose->aspell_options_menu = parent_item;
+
+       spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
+
+       spell_menu = g_slist_reverse(spell_menu);
+       for (items = spell_menu;
+            items; items = items->next) {
+               menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
+               gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
+               gtk_widget_show(GTK_WIDGET(menuitem));
+       }
+       g_slist_free(spell_menu);
+
+       gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
+       
+}
+#endif
+
+static gboolean compose_force_window_origin = TRUE;
 static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
                                                 gboolean batch)
 {
@@ -5614,6 +5750,8 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
        compose->mutex = g_mutex_new();
        compose->set_cursor_pos = -1;
 
+       compose->tooltips = gtk_tooltips_new();
+
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
        gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
@@ -5631,7 +5769,9 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
        }
        gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
                                      &geometry, GDK_HINT_MIN_SIZE);
-       gtk_widget_set_uposition(window, prefs_common.compose_x, 
+       
+       if (compose_force_window_origin)
+               gtk_widget_set_uposition(window, prefs_common.compose_x, 
                                 prefs_common.compose_y);
 
        g_signal_connect(G_OBJECT(window), "delete_event",
@@ -5879,7 +6019,9 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
                                                  prefs_common.recheck_when_changing_dict,
                                                  prefs_common.use_alternate,
                                                  GTK_TEXT_VIEW(text),
-                                                 GTK_WINDOW(compose->window));
+                                                 GTK_WINDOW(compose->window),
+                                                 compose_spell_menu_changed,
+                                                 compose);
                        if (!gtkaspell) {
                                alertpanel_error(_("Spell checker could not "
                                                "be started.\n%s"),
@@ -5899,6 +6041,7 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
                }
        }
         compose->gtkaspell = gtkaspell;
+       compose_spell_menu_changed(compose);
 #endif
 
        compose_select_account(compose, account, TRUE);
@@ -5963,45 +6106,54 @@ static Compose *compose_create(PrefsAccount *account, ComposeMode mode,
 static GtkWidget *compose_account_option_menu_create(Compose *compose)
 {
        GList *accounts;
+       GtkWidget *hbox;
        GtkWidget *optmenu;
        GtkWidget *menu;
-       gint num = 0, def_menu = 0;
+       GtkWidget *from_name = NULL;
 
+       gint num = 0, def_menu = 0;
+       
        accounts = account_get_list();
        g_return_val_if_fail(accounts != NULL, NULL);
 
        optmenu = gtk_option_menu_new();
        menu = gtk_menu_new();
 
+       hbox = gtk_hbox_new(FALSE, 6);
+       from_name = gtk_entry_new();
+       
+       g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
+                        G_CALLBACK(compose_grab_focus_cb), compose);
+
        for (; accounts != NULL; accounts = accounts->next, num++) {
                PrefsAccount *ac = (PrefsAccount *)accounts->data;
                GtkWidget *menuitem;
-               gchar *name;
+               gchar *name, *from = NULL;
 
                if (ac == compose->account) def_menu = num;
 
-               if (prefs_common.compose_no_markup) {
-                       if (ac->name)
-                               name = g_markup_printf_escaped("%s : %s &lt;%s&gt;",
-                                                      ac->account_name,
-                                                      ac->name, ac->address);
-                       else
-                               name = g_markup_printf_escaped("%s : &lt;%s&gt;",
-                                                      ac->account_name, ac->address);
-               } else {
-                       if (ac->name)
-                               name = g_markup_printf_escaped("<i>%s</i> : %s &lt;<b>%s</b>&gt;",
-                                                      ac->account_name,
-                                                      ac->name, ac->address);
-                       else
-                               name = g_markup_printf_escaped("<i>%s</i> : &lt;<b>%s</b>&gt;",
-                                                      ac->account_name, ac->address);
+               name = g_markup_printf_escaped("From: <i>%s</i>",
+                                      ac->account_name);
+               
+               if (ac == compose->account) {
+                       if (ac->name && *ac->name) {
+                               gchar *buf;
+                               QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
+                               from = g_strdup_printf("%s <%s>",
+                                                      buf, ac->address);
+                               gtk_entry_set_text(GTK_ENTRY(from_name), from);
+                       } else {
+                               from = g_strdup_printf("%s",
+                                                      ac->address);
+                               gtk_entry_set_text(GTK_ENTRY(from_name), from);
+                       }
                }
                MENUITEM_ADD(menu, menuitem, name, ac->account_id);
                gtk_label_set_use_markup (
                                GTK_LABEL (gtk_bin_get_child (GTK_BIN (menuitem))),
                                TRUE);
                g_free(name);
+               g_free(from);
                g_signal_connect(G_OBJECT(menuitem), "activate",
                                 G_CALLBACK(account_activated),
                                 compose);
@@ -6010,7 +6162,17 @@ static GtkWidget *compose_account_option_menu_create(Compose *compose)
        gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
        gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), def_menu);
 
-       return optmenu;
+       gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
+       
+       gtk_tooltips_set_tip(compose->tooltips, optmenu,
+               _("Account to use for this email"), NULL);
+       gtk_tooltips_set_tip(compose->tooltips, from_name,
+               _("Sender address to be used"), NULL);
+
+       compose->from_name = from_name;
+       
+       return hbox;
 }
 
 static void compose_set_priority_cb(gpointer data,
@@ -7376,6 +7538,7 @@ static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
        /* Save draft infos */
        fprintf(fp, "X-Sylpheed-Account-Id:%d\n", compose->account->account_id);
        fprintf(fp, "S:%s\n", compose->account->address);
+
        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
                gchar *savefolderid;
 
@@ -8628,10 +8791,10 @@ static void compose_add_field_list( Compose *compose, GList *listAddress ) {
        }
 }
 
-void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
-                                   guint action)
+static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
+                                   guint action, gboolean opening_multiple)
 {
-       gchar *body;
+       gchar *body = NULL;
        GSList *new_msglist = NULL;
        MsgInfo *tmp_msginfo = NULL;
        gboolean originally_enc = FALSE;
@@ -8641,7 +8804,7 @@ void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
 
        g_return_if_fail(msginfo_list != NULL);
 
-       if (g_slist_length(msginfo_list) == 1) {
+       if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
                MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
                MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
                
@@ -8658,7 +8821,8 @@ void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
                }
        }
 
-       body = messageview_get_selection(msgview);
+       if (!opening_multiple)
+               body = messageview_get_selection(msgview);
 
        if (new_msglist) {
                compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
@@ -8674,6 +8838,41 @@ void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list,
        g_free(body);
 }
 
+void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
+                                   guint action)
+{
+       if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
+       &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
+               GSList *cur = msginfo_list;
+               gchar *msg = g_strdup_printf(_("You are about to reply to %d "
+                                              "messages. Opening the windows "
+                                              "could take some time. Do you "
+                                              "want to continue?"), 
+                                              g_slist_length(msginfo_list));
+               if (g_slist_length(msginfo_list) > 9
+               &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
+                   != G_ALERTALTERNATE) {
+                       g_free(msg);
+                       return;
+               }
+               g_free(msg);
+               /* We'll open multiple compose windows */
+               /* let the WM place the next windows */
+               compose_force_window_origin = FALSE;
+               for (; cur; cur = cur->next) {
+                       GSList tmplist;
+                       tmplist.data = cur->data;
+                       tmplist.next = NULL;
+                       compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
+               }
+               compose_force_window_origin = TRUE;
+       } else {
+               /* forwarding multiple mails as attachments is done via a
+                * single compose window */
+               compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
+       }
+}
+
 void compose_set_position(Compose *compose, gint pos)
 {
        GtkTextView *text = GTK_TEXT_VIEW(compose->text);