X-Git-Url: http://git.claws-mail.org/?p=claws.git;a=blobdiff_plain;f=src%2Fcompose.c;h=4520069acd82f1cf45f0c54c2dad9e1e8d852a76;hp=ca3532d6f4e5adef07414ff28a1148f79439c4d5;hb=d0d41b845f48c57f7c9fbe65e506e296b8678160;hpb=9015d4ea1e1b27135ba91755278560bd79ff76fd diff --git a/src/compose.c b/src/compose.c index ca3532d6f..4520069ac 100644 --- a/src/compose.c +++ b/src/compose.c @@ -1,6 +1,6 @@ /* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team + * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,7 @@ #ifdef HAVE_CONFIG_H # include "config.h" +#include "claws-features.h" #endif #include "defs.h" @@ -60,7 +61,12 @@ #include "main.h" #include "mainwindow.h" #include "compose.h" -#include "addressbook.h" +#ifndef USE_NEW_ADDRBOOK + #include "addressbook.h" +#else + #include "addressbook-dbus.h" + #include "addressadd.h" +#endif #include "folderview.h" #include "procmsg.h" #include "menu.h" @@ -83,11 +89,12 @@ #include "codeconv.h" #include "utils.h" #include "gtkutils.h" +#include "gtkshruler.h" #include "socket.h" #include "alertpanel.h" #include "manage_window.h" -#include "gtkshruler.h" #include "folder.h" +#include "folder_item_prefs.h" #include "addr_compl.h" #include "quote_fmt.h" #include "undo.h" @@ -176,11 +183,12 @@ typedef enum { #define MAX_REFERENCES_LEN 999 static GList *compose_list = NULL; +static GSList *extra_headers = NULL; static Compose *compose_generic_new (PrefsAccount *account, const gchar *to, FolderItem *item, - GPtrArray *attach_files, + GList *attach_files, GList *listAddress ); static Compose *compose_create (PrefsAccount *account, @@ -220,6 +228,9 @@ static MailField compose_entries_set (Compose *compose, ComposeEntryType to_type); static gint compose_parse_header (Compose *compose, MsgInfo *msginfo); +static gint compose_parse_manual_headers (Compose *compose, + MsgInfo *msginfo, + HeaderEntry *entries); static gchar *compose_parse_references (const gchar *ref, const gchar *msgid); @@ -291,6 +302,7 @@ static gint compose_queue_sub (Compose *compose, static int compose_add_attachments (Compose *compose, MimeInfo *parent); static gchar *compose_get_header (Compose *compose); +static gchar *compose_get_manual_headers_info (Compose *compose); static void compose_convert_header (Compose *compose, gchar *dest, @@ -351,6 +363,9 @@ static void compose_add_field_list ( Compose *compose, /* callback functions */ +static void compose_notebook_size_alloc (GtkNotebook *notebook, + GtkAllocation *allocation, + Compose *compose); static gboolean compose_edit_size_alloc (GtkEditable *widget, GtkAllocation *allocation, GtkSHRuler *shruler); @@ -381,6 +396,8 @@ static void compose_insert_sig_cb (GtkAction *action, static void compose_close_cb (GtkAction *action, gpointer data); +static void compose_print_cb (GtkAction *action, + gpointer data); static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data); @@ -487,6 +504,10 @@ static gboolean compose_drag_drop (GtkWidget *widget, GdkDragContext *drag_context, gint x, gint y, guint time, gpointer user_data); +static gboolean completion_set_focus_to_subject + (GtkWidget *widget, + GdkEventKey *event, + Compose *user_data); static void text_inserted (GtkTextBuffer *buffer, GtkTextIter *iter, @@ -525,7 +546,6 @@ static void compose_check_backwards (GtkAction *action, gpointer data); static void compose_check_forwards_go (GtkAction *action, gpointer data); #endif -static gint compose_defer_auto_save_draft (Compose *compose); static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo); static MsgInfo *compose_msginfo_new_from_compose(Compose *compose); @@ -570,6 +590,8 @@ static GtkActionEntry compose_entries[] = /* {"Message/---", NULL, "---" }, */ {"Message/Save", NULL, N_("_Save"), "S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/ /* {"Message/---", NULL, "---" }, */ + {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) }, + /* {"Message/---", NULL, "---" }, */ {"Message/Close", NULL, N_("_Close"), "W", NULL, G_CALLBACK(compose_close_cb) }, /* Edit menu */ @@ -581,10 +603,10 @@ static GtkActionEntry compose_entries[] = {"Edit/Copy", NULL, N_("_Copy"), "C", NULL, G_CALLBACK(compose_copy_cb) }, {"Edit/Paste", NULL, N_("_Paste"), "V", NULL, G_CALLBACK(compose_paste_cb) }, - {"Edit/SpecialPaste", NULL, N_("Special paste") }, - {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) }, - {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) }, - {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) }, + {"Edit/SpecialPaste", NULL, N_("_Special paste") }, + {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) }, + {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) }, + {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) }, {"Edit/SelectAll", NULL, N_("Select _all"), "A", NULL, G_CALLBACK(compose_allsel_cb) }, @@ -787,10 +809,12 @@ static void compose_create_tags(GtkTextView *text, Compose *compose) { GtkTextBuffer *buffer; GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0}; +#if !GTK_CHECK_VERSION(2, 24, 0) GdkColormap *cmap; - GdkColor color[8]; gboolean success[8]; int i; + GdkColor color[8]; +#endif buffer = gtk_text_view_get_buffer(text); @@ -852,6 +876,7 @@ static void compose_create_tags(GtkTextView *text, Compose *compose) compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL); compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL); +#if !GTK_CHECK_VERSION(2, 24, 0) color[0] = quote_color1; color[1] = quote_color2; color[2] = quote_color3; @@ -860,24 +885,23 @@ static void compose_create_tags(GtkTextView *text, Compose *compose) color[5] = quote_bgcolor3; color[6] = signature_color; color[7] = uri_color; - cmap = gdk_drawable_get_colormap(compose->window->window); + + cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window)); gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success); for (i = 0; i < 8; i++) { if (success[i] == FALSE) { - GtkStyle *style; - g_warning("Compose: color allocation failed.\n"); - 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; } } +#endif } Compose *compose_new(PrefsAccount *account, const gchar *mailto, - GPtrArray *attach_files) + GList *attach_files) { return compose_generic_new(account, mailto, NULL, attach_files, NULL); } @@ -937,7 +961,7 @@ static gchar *compose_get_save_to(Compose *compose) } Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item, - GPtrArray *attach_files, GList *listAddress ) + GList *attach_files, GList *listAddress ) { Compose *compose; GtkTextView *textview; @@ -948,6 +972,7 @@ Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderI gchar *mailto_from = NULL; PrefsAccount *mailto_account = NULL; MsgInfo* dummyinfo = NULL; + gint cursor_pos = -1; MailField mfield = NO_FIELD_PRESENT; gchar* buf; GtkTextMark *mark; @@ -991,7 +1016,7 @@ Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderI dummyinfo = compose_msginfo_new_from_compose(compose); /* decode \-escape sequences in the internal representation of the quote format */ - tmp = malloc(strlen(item->prefs->compose_override_from_format)+1); + tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1); pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format); #ifdef USE_ENCHANT @@ -1082,7 +1107,7 @@ Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderI dummyinfo = compose_msginfo_new_from_compose(compose); /* decode \-escape sequences in the internal representation of the quote format */ - tmp = malloc(strlen(subject_format)+1); + tmp = g_malloc(strlen(subject_format)+1); pref_get_unescaped_pref(tmp, subject_format); subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1); @@ -1144,12 +1169,13 @@ Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderI procmsg_msginfo_free( dummyinfo ); if (attach_files) { - gint i; - gchar *file; + GList *curr; + AttachInfo *ainfo; - for (i = 0; i < attach_files->len; i++) { - file = g_ptr_array_index(attach_files, i); - compose_attach_append(compose, file, file, NULL, NULL); + for (curr = attach_files ; curr != NULL ; curr = curr->next) { + ainfo = (AttachInfo *) curr->data; + compose_attach_append(compose, ainfo->file, ainfo->name, + ainfo->content_type, ainfo->charset); } } @@ -1198,7 +1224,11 @@ Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderI * is therefore created before placing the cursor */ case BODY_FIELD_PRESENT: - gtk_widget_grab_focus(compose->text); + cursor_pos = quote_fmt_get_cursor_pos(); + if (cursor_pos == -1) + gtk_widget_grab_focus(compose->header_last->entry); + else + gtk_widget_grab_focus(compose->text); break; } @@ -1213,7 +1243,7 @@ Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderI compose->modified = FALSE; compose_set_title(compose); - hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose); + hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose); return compose; } @@ -1548,7 +1578,7 @@ static Compose *compose_generic_reply(MsgInfo *msginfo, gchar *buf = NULL; /* decode \-escape sequences in the internal representation of the quote format */ - tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1); + tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1); pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format); #ifdef USE_ENCHANT @@ -1576,7 +1606,8 @@ static Compose *compose_generic_reply(MsgInfo *msginfo, undo_block(compose->undostruct); #ifdef USE_ENCHANT - compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder); + compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder); + gtkaspell_block_check(compose->gtkaspell); #endif if (quote_mode == COMPOSE_QUOTE_FORCED || @@ -1612,10 +1643,6 @@ static Compose *compose_generic_reply(MsgInfo *msginfo, _("The body of the \"Reply\" template has an error at line %d.")); compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE); quote_fmt_reset_vartable(); -#ifdef USE_ENCHANT - if (compose->gtkaspell && compose->gtkaspell->check_while_typing) - gtkaspell_highlight_all(compose->gtkaspell); -#endif } if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) { @@ -1635,6 +1662,11 @@ static Compose *compose_generic_reply(MsgInfo *msginfo, compose_wrap_all(compose); +#ifdef USE_ENCHANT + if (compose->gtkaspell && compose->gtkaspell->check_while_typing) + gtkaspell_highlight_all(compose->gtkaspell); + gtkaspell_unblock_check(compose->gtkaspell); +#endif SIGNAL_UNBLOCK(textbuf); gtk_widget_grab_focus(compose->text); @@ -1675,7 +1707,7 @@ Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo, Compose *compose; GtkTextView *textview; GtkTextBuffer *textbuf; - GtkTextIter iter; + gint cursor_pos = -1; ComposeMode mode; cm_return_val_if_fail(msginfo != NULL, NULL); @@ -1729,10 +1761,11 @@ Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo, full_msginfo = procmsg_msginfo_copy(msginfo); /* decode \-escape sequences in the internal representation of the quote format */ - tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1); + tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1); pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format); #ifdef USE_ENCHANT + gtkaspell_block_check(compose->gtkaspell); quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE, compose->gtkaspell); #else @@ -1773,11 +1806,6 @@ Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo, const gchar *body_fmt = NULL; MsgInfo *full_msginfo; - if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt) - body_fmt = gettext(prefs_common.fw_quotefmt); - else - body_fmt = ""; - full_msginfo = procmsg_msginfo_get_full_info(msginfo); if (!full_msginfo) full_msginfo = procmsg_msginfo_copy(msginfo); @@ -1814,10 +1842,6 @@ Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo, compose_attach_parts(compose, msginfo); procmsg_msginfo_free(full_msginfo); -#ifdef USE_ENCHANT - if (compose->gtkaspell && compose->gtkaspell->check_while_typing) - gtkaspell_highlight_all(compose->gtkaspell); -#endif } SIGNAL_BLOCK(textbuf); @@ -1827,12 +1851,18 @@ Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo, compose_wrap_all(compose); +#ifdef USE_ENCHANT + if (compose->gtkaspell && compose->gtkaspell->check_while_typing) + gtkaspell_highlight_all(compose->gtkaspell); + gtkaspell_unblock_check(compose->gtkaspell); +#endif SIGNAL_UNBLOCK(textbuf); - gtk_text_buffer_get_start_iter(textbuf, &iter); - gtk_text_buffer_place_cursor(textbuf, &iter); - - gtk_widget_grab_focus(compose->header_last->entry); + cursor_pos = quote_fmt_get_cursor_pos(); + if (cursor_pos == -1) + gtk_widget_grab_focus(compose->header_last->entry); + else + gtk_widget_grab_focus(compose->text); if (!no_extedit && prefs_common.auto_exteditor) compose_exec_ext_editor(compose); @@ -1924,7 +1954,7 @@ static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_ gchar *buf = NULL; /* decode \-escape sequences in the internal representation of the quote format */ - tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1); + tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1); pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format); #ifdef USE_ENCHANT @@ -2108,6 +2138,7 @@ Compose *compose_reedit(MsgInfo *msginfo, gboolean batch) MsgInfo *replyinfo = NULL, *fwdinfo = NULL; gboolean autowrap = prefs_common.autowrap; gboolean autoindent = prefs_common.auto_indent; + HeaderEntry *manual_headers = NULL; cm_return_val_if_fail(msginfo != NULL, NULL); cm_return_val_if_fail(msginfo->folder != NULL, NULL); @@ -2214,6 +2245,15 @@ Compose *compose_reedit(MsgInfo *msginfo, gboolean batch) } g_strfreev(tokens); } + /* Get manual headers */ + if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) { + gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]); + if (*listmh != '\0') { + debug_print("Got manual headers: %s\n", listmh); + manual_headers = procheader_entries_from_str(listmh); + } + g_free(listmh); + } } else { account = msginfo->folder->folder->account; } @@ -2303,8 +2343,8 @@ Compose *compose_reedit(MsgInfo *msginfo, gboolean batch) } if (fp != NULL) { - gboolean prev_autowrap = compose->autowrap; - GtkTextBuffer *buffer = textbuf; + gboolean prev_autowrap; + GtkTextBuffer *buffer; BLOCK_WRAP(); while (fgets(buf, sizeof(buf), fp) != NULL) { strcrchomp(buf); @@ -2322,6 +2362,16 @@ Compose *compose_reedit(MsgInfo *msginfo, gboolean batch) G_CALLBACK(compose_changed_cb), compose); + if (manual_headers != NULL) { + if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) { + procheader_entries_free(manual_headers); + compose->updating = FALSE; + compose_destroy(compose); + return NULL; + } + procheader_entries_free(manual_headers); + } + gtk_widget_grab_focus(compose->text); if (prefs_common.auto_exteditor) { @@ -2517,8 +2567,6 @@ void compose_entry_append(Compose *compose, const gchar *address, gchar *tmp = g_strdup(begin); gchar *o_tmp = tmp; tmp[cur-begin]='\0'; - cur++; - begin = cur; while (*tmp == ' ' || *tmp == '\t') tmp++; compose_add_header_entry(compose, header, tmp, pref_type); @@ -2528,12 +2576,18 @@ void compose_entry_append(Compose *compose, const gchar *address, static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto) { +#if !GTK_CHECK_VERSION(3, 0, 0) static GdkColor yellow; static GdkColor black; static gboolean yellow_initialised = FALSE; +#else + static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe }; + static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 }; +#endif GSList *h_list; GtkEntry *entry; +#if !GTK_CHECK_VERSION(3, 0, 0) if (!yellow_initialised) { gdk_color_parse("#f5f6be", &yellow); gdk_color_parse("#000000", &black); @@ -2542,19 +2596,24 @@ static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto) yellow_initialised &= gdk_colormap_alloc_color( gdk_colormap_get_system(), &black, FALSE, TRUE); } +#endif for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) { entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry); if (gtk_entry_get_text(entry) && !g_utf8_collate(gtk_entry_get_text(entry), mailto)) { +#if !GTK_CHECK_VERSION(3, 0, 0) if (yellow_initialised) { +#endif gtk_widget_modify_base( GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry), GTK_STATE_NORMAL, &yellow); gtk_widget_modify_text( GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry), GTK_STATE_NORMAL, &black); +#if !GTK_CHECK_VERSION(3, 0, 0) } +#endif } } } @@ -2745,13 +2804,13 @@ static gint compose_parse_header(Compose *compose, MsgInfo *msginfo) if (hentry[H_REPLY_TO].body[0] != '\0') { compose->replyto = conv_unmime_header(hentry[H_REPLY_TO].body, - NULL); + NULL, TRUE); } g_free(hentry[H_REPLY_TO].body); hentry[H_REPLY_TO].body = NULL; } if (hentry[H_CC].body != NULL) { - compose->cc = conv_unmime_header(hentry[H_CC].body, NULL); + compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE); g_free(hentry[H_CC].body); hentry[H_CC].body = NULL; } @@ -2768,7 +2827,7 @@ static gint compose_parse_header(Compose *compose, MsgInfo *msginfo) if (hentry[H_BCC].body != NULL) { if (compose->mode == COMPOSE_REEDIT) compose->bcc = - conv_unmime_header(hentry[H_BCC].body, NULL); + conv_unmime_header(hentry[H_BCC].body, NULL, TRUE); g_free(hentry[H_BCC].body); hentry[H_BCC].body = NULL; } @@ -2780,7 +2839,7 @@ static gint compose_parse_header(Compose *compose, MsgInfo *msginfo) if (hentry[H_FOLLOWUP_TO].body[0] != '\0') { compose->followup_to = conv_unmime_header(hentry[H_FOLLOWUP_TO].body, - NULL); + NULL, TRUE); } g_free(hentry[H_FOLLOWUP_TO].body); hentry[H_FOLLOWUP_TO].body = NULL; @@ -2851,6 +2910,33 @@ static gint compose_parse_header(Compose *compose, MsgInfo *msginfo) return 0; } +static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries) +{ + FILE *fp; + HeaderEntry *he; + + cm_return_val_if_fail(msginfo != NULL, -1); + + if ((fp = procmsg_open_message(msginfo)) == NULL) return -1; + procheader_get_header_fields(fp, entries); + fclose(fp); + + he = entries; + while (he != NULL && he->name != NULL) { + GtkTreeIter iter; + GtkListStore *model = NULL; + + debug_print("Adding manual header: %s with value %s\n", he->name, he->body); + model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo))); + COMBOBOX_ADD(model, he->name, COMPOSE_TO); + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter); + gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body); + ++he; + } + + return 0; +} + static gchar *compose_parse_references(const gchar *ref, const gchar *msgid) { GSList *ref_id_list, *cur; @@ -2877,8 +2963,7 @@ static gchar *compose_parse_references(const gchar *ref, const gchar *msgid) ref_id_list = g_slist_remove (ref_id_list, ref_id_list->next->data); } else { - slist_free_strings(ref_id_list); - g_slist_free(ref_id_list); + slist_free_strings_full(ref_id_list); return NULL; } } else @@ -2892,8 +2977,7 @@ static gchar *compose_parse_references(const gchar *ref, const gchar *msgid) g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data); } - slist_free_strings(ref_id_list); - g_slist_free(ref_id_list); + slist_free_strings_full(ref_id_list); new_ref_str = new_ref->str; g_string_free(new_ref, FALSE); @@ -2959,7 +3043,7 @@ static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo, gchar *tmp = NULL; /* decode \-escape sequences in the internal representation of the quote format */ - tmp = malloc(strlen(fmt)+1); + tmp = g_malloc(strlen(fmt)+1); pref_get_unescaped_pref(tmp, fmt); quote_fmt_scan_string(tmp); quote_fmt_parse(); @@ -3268,8 +3352,7 @@ static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo, g_free(addr); } - slist_free_strings(cc_list); - g_slist_free(cc_list); + slist_free_strings_full(cc_list); } g_free(ac_email); @@ -4535,7 +4618,7 @@ end: *par_iter = iter; undo_wrapping(compose->undostruct, FALSE); compose->autowrap = prev_autowrap; - + return modified; } @@ -4757,12 +4840,10 @@ gboolean compose_check_for_valid_recipient(Compose *compose) { gchar **strptr; /* free to and newsgroup list */ - slist_free_strings(compose->to_list); - g_slist_free(compose->to_list); + slist_free_strings_full(compose->to_list); compose->to_list = NULL; - slist_free_strings(compose->newsgroup_list); - g_slist_free(compose->newsgroup_list); + slist_free_strings_full(compose->newsgroup_list); compose->newsgroup_list = NULL; /* search header entries for to and newsgroup entries */ @@ -4983,10 +5064,10 @@ gint compose_send(Compose *compose) } if (msgpath == NULL) { msgpath = folder_item_fetch_msg(folder, msgnum); - val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed); + val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed); g_free(msgpath); } else { - val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed); + val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed); claws_unlink(msgpath); g_free(msgpath); } @@ -5665,17 +5746,13 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, FILE *fp; GSList *cur; gint num; - static gboolean lock = FALSE; PrefsAccount *mailac = NULL, *newsac = NULL; gboolean err = FALSE; debug_print("queueing message...\n"); cm_return_val_if_fail(compose->account != NULL, -1); - lock = TRUE; - if (compose_check_entries(compose, check_subject) == FALSE) { - lock = FALSE; if (compose->batch) { gtk_widget_show_all(compose->window); } @@ -5684,7 +5761,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, if (!compose->to_list && !compose->newsgroup_list) { g_warning("can't get recipient list."); - lock = FALSE; return -1; } @@ -5694,7 +5770,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, else if (cur_account && cur_account->protocol != A_NNTP) mailac = cur_account; else if (!(mailac = compose_current_mail_account())) { - lock = FALSE; alertpanel_error(_("No account for sending mails available!")); return -1; } @@ -5704,7 +5779,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, if (compose->account->protocol == A_NNTP) newsac = compose->account; else { - lock = FALSE; alertpanel_error(_("Selected account isn't NNTP: Posting is impossible.")); return -1; } @@ -5717,7 +5791,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, if ((fp = g_fopen(tmp, "wb")) == NULL) { FILE_OP_ERROR(tmp, "fopen"); g_free(tmp); - lock = FALSE; return -2; } @@ -5779,7 +5852,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, if (compose->use_encryption) { gchar *encdata; if (!compose_warn_encryption(compose)) { - lock = FALSE; fclose(fp); claws_unlink(tmp); g_free(tmp); @@ -5803,7 +5875,8 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0); /* and if encdata was null, it means there's been a problem in * key selection */ - lock = FALSE; + if (err == TRUE) + g_warning("failed to write queue message"); fclose(fp); claws_unlink(tmp); g_free(tmp); @@ -5850,7 +5923,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, if (compose->redirect_filename != NULL) { if (compose_redirect_write_to_file(compose, fp) < 0) { - lock = FALSE; fclose(fp); claws_unlink(tmp); g_free(tmp); @@ -5859,7 +5931,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, } else { gint result = 0; if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) { - lock = FALSE; fclose(fp); claws_unlink(tmp); g_free(tmp); @@ -5871,14 +5942,12 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, fclose(fp); claws_unlink(tmp); g_free(tmp); - lock = FALSE; return -2; } if (fclose(fp) == EOF) { FILE_OP_ERROR(tmp, "fclose"); claws_unlink(tmp); g_free(tmp); - lock = FALSE; return -2; } @@ -5891,7 +5960,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, g_warning("can't find queue folder\n"); claws_unlink(tmp); g_free(tmp); - lock = FALSE; return -1; } folder_item_scan(queue); @@ -5899,7 +5967,6 @@ static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, g_warning("can't queue the message\n"); claws_unlink(tmp); g_free(tmp); - lock = FALSE; return -1; } @@ -5976,7 +6043,7 @@ static int compose_add_attachments(Compose *compose, MimeInfo *parent) !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) { mimepart->disposition = DISPOSITIONTYPE_INLINE; } else if (mimepart->type == MIMETYPE_TEXT) { - if (!ainfo->name && compose->mode == COMPOSE_FORWARD_INLINE) { + if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) { /* Text parts with no name come from multipart/alternative * forwards. Make sure the recipient won't look at the * original HTML part by mistake. */ @@ -6078,6 +6145,55 @@ static void compose_add_headerfield_from_headerlist(Compose *compose, return; } +static gchar *compose_get_manual_headers_info(Compose *compose) +{ + GString *sh_header = g_string_new(" "); + GSList *list; + gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL}; + + for (list = compose->header_list; list; list = list->next) { + ComposeHeaderEntry *headerentry; + gchar *tmp; + gchar *headername; + gchar *headername_wcolon; + const gchar *headername_trans; + gchar **string; + gboolean standard_header = FALSE; + + headerentry = ((ComposeHeaderEntry *)list->data); + + tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))))); + g_strstrip(tmp); + if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) { + g_free(tmp); + continue; + } + + if (!strstr(tmp, ":")) { + headername_wcolon = g_strconcat(tmp, ":", NULL); + headername = g_strdup(tmp); + } else { + headername_wcolon = g_strdup(tmp); + headername = g_strdup(strtok(tmp, ":")); + } + g_free(tmp); + + string = std_headers; + while (*string != NULL) { + headername_trans = prefs_common_translated_header_name(*string); + if (!strcmp(headername_trans, headername_wcolon)) + standard_header = TRUE; + string++; + } + if (!standard_header && !IS_IN_CUSTOM_HEADER(headername)) + g_string_append_printf(sh_header, "%s ", headername); + g_free(headername); + g_free(headername_wcolon); + } + g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */ + return g_string_free(sh_header, FALSE); +} + static gchar *compose_get_header(Compose *compose) { gchar buf[BUFFSIZE]; @@ -6227,7 +6343,8 @@ static gchar *compose_get_header(Compose *compose) } /* Program version and system info */ - if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") && + if (compose->account->gen_xmailer && + g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") && !compose->newsgroup_list) { g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n", prog_version, @@ -6249,10 +6366,12 @@ static gchar *compose_get_header(Compose *compose) cur = cur->next) { CustomHeader *chdr = (CustomHeader *)cur->data; - if (custom_header_is_allowed(chdr->name)) { + if (custom_header_is_allowed(chdr->name) + && chdr->value != NULL + && *(chdr->value) != '\0') { compose_convert_header (compose, buf, sizeof(buf), - chdr->value ? chdr->value : "", + chdr->value, strlen(chdr->name) + 2, FALSE); g_string_append_printf(header, "%s: %s\n", chdr->name, buf); } @@ -6436,7 +6555,14 @@ static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_d if (*address != '\0') { gchar *name = procheader_get_fromname(address); extract_address(address); +#ifndef USE_NEW_ADDRBOOK addressbook_add_contact(name, address, NULL, NULL); +#else + debug_print("%s: %s\n", name, address); + if (addressadd_selection(name, address, NULL, NULL)) { + debug_print( "addressbook_add_contact - added\n" ); + } +#endif } g_free(address); } @@ -6467,6 +6593,54 @@ static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer gtk_widget_show(menuitem); } +void compose_add_extra_header(gchar *header, GtkListStore *model) +{ + GtkTreeIter iter; + if (strcmp(header, "")) { + COMBOBOX_ADD(model, header, COMPOSE_TO); + } +} + +void compose_add_extra_header_entries(GtkListStore *model) +{ + FILE *exh; + gchar *exhrc; + gchar buf[BUFFSIZE]; + gint lastc; + + if (extra_headers == NULL) { + exhrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "extraheaderrc", NULL); + if ((exh = g_fopen(exhrc, "rb")) == NULL) { + debug_print("extra headers file not found\n"); + goto extra_headers_done; + } + while (fgets(buf, BUFFSIZE, exh) != NULL) { + lastc = strlen(buf) - 1; /* remove trailing \n */ + buf[lastc] = (buf[lastc] == '\n')? '\0': buf[lastc]; + --lastc; + if (lastc > 0 && buf[0] != '#' && buf[lastc] == ':') { + buf[lastc] = '\0'; /* remove trailing : for comparison */ + if (custom_header_is_allowed(buf)) { + buf[lastc] = ':'; + extra_headers = g_slist_prepend(extra_headers, g_strdup(buf)); + } + else + g_message("disallowed extra header line: %s\n", buf); + } + else { + if (buf[0] != '#') + g_message("invalid extra header line: %s\n", buf); + } + } + fclose(exh); +extra_headers_done: + g_free(exhrc); + extra_headers = g_slist_prepend(extra_headers, g_strdup("")); /* end of list */ + extra_headers = g_slist_reverse(extra_headers); + } + g_slist_foreach(extra_headers, (GFunc)compose_add_extra_header, (gpointer)model); +} + static void compose_create_header_entry(Compose *compose) { gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL}; @@ -6502,11 +6676,18 @@ static void compose_create_header_entry(Compose *compose) COMPOSE_REPLYTO); COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"), COMPOSE_FOLLOWUPTO); + compose_add_extra_header_entries(model); gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); - g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus", + g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus", G_CALLBACK(compose_grab_focus_cb), compose); gtk_widget_show(combo); + + GList *l = NULL; + l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo))); + gtk_container_set_focus_chain(GTK_CONTAINER(combo), l); + g_list_free(l); + gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1, compose->header_nextrow, compose->header_nextrow+1, GTK_SHRINK, GTK_FILL, 0, 0); @@ -6680,12 +6861,27 @@ static void compose_remove_header_entries(Compose *compose) static GtkWidget *compose_create_header(Compose *compose) { GtkWidget *from_optmenu_hbox; + GtkWidget *header_scrolledwin_main; + GtkWidget *header_table_main; GtkWidget *header_scrolledwin; GtkWidget *header_table; - gint count = 0; + /* parent with account selection and from header */ + header_scrolledwin_main = gtk_scrolled_window_new(NULL, NULL); + gtk_widget_show(header_scrolledwin_main); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin_main), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - /* header labels and entries */ + header_table_main = gtk_table_new(2, 2, FALSE); + gtk_widget_show(header_table_main); + gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin_main), header_table_main); + gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin_main)))), GTK_SHADOW_NONE); + + from_optmenu_hbox = compose_account_option_menu_create(compose); + gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox, + 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0); + + /* child with header labels and entries */ header_scrolledwin = gtk_scrolled_window_new(NULL, NULL); gtk_widget_show(header_scrolledwin); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); @@ -6695,23 +6891,19 @@ static GtkWidget *compose_create_header(Compose *compose) gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table); gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE); - count = 0; - /* option menu for selecting accounts */ - from_optmenu_hbox = compose_account_option_menu_create(compose); - gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox, - 0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0); - count++; + gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin, + 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2); compose->header_table = header_table; compose->header_list = NULL; - compose->header_nextrow = count; + compose->header_nextrow = 0; compose_create_header_entry(compose); - compose->table = NULL; + compose->table = NULL; - return header_scrolledwin ; + return header_scrolledwin_main; } static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data) @@ -6869,8 +7061,6 @@ static GtkWidget *compose_create_others(Compose *compose) G_CALLBACK(compose_savemsg_select_cb), compose); - rowcount++; - return table; } @@ -6902,7 +7092,7 @@ static gboolean text_clicked(GtkWidget *text, GdkEventButton *event, Compose *compose) { gint prev_autowrap; - GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); #if USE_ENCHANT if (event->button == 3) { GtkTextIter iter; @@ -7057,11 +7247,9 @@ static Compose *compose_create(PrefsAccount *account, GtkWidget *text; GtkTextBuffer *buffer; GtkClipboard *clipboard; - CLAWS_TIP_DECL(); UndoMain *undostruct; - gchar *titles[N_ATTACH_COLS]; GtkWidget *popupmenu; GtkWidget *tmpl_menu; GtkActionGroup *action_group = NULL; @@ -7077,16 +7265,11 @@ static Compose *compose_create(PrefsAccount *account, debug_print("Creating compose window...\n"); compose = g_new0(Compose, 1); - titles[COL_MIMETYPE] = _("MIME type"); - titles[COL_SIZE] = _("Size"); - titles[COL_NAME] = _("Name"); - titles[COL_CHARSET] = _("Charset"); - compose->batch = batch; compose->account = account; compose->folder = folder; - compose->mutex = g_mutex_new(); + compose->mutex = cm_mutex_new(); compose->set_cursor_pos = -1; #if !(GTK_CHECK_VERSION(2,12,0)) @@ -7096,7 +7279,8 @@ static Compose *compose_create(PrefsAccount *account, window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose"); gtk_window_set_resizable(GTK_WINDOW(window), TRUE); - gtk_widget_set_size_request(window, -1, prefs_common.compose_height); + gtk_widget_set_size_request(window, prefs_common.compose_width, + prefs_common.compose_height); if (!geometry.max_width) { geometry.max_width = gdk_screen_width(); @@ -7164,6 +7348,8 @@ static Compose *compose_create(PrefsAccount *account, MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR) MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM) MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR) + MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM) + MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR) MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM) /* Edit menu */ @@ -7350,7 +7536,7 @@ static Compose *compose_create(PrefsAccount *account, /* Notebook */ notebook = gtk_notebook_new(); - gtk_widget_set_size_request(notebook, -1, 130); + gtk_widget_set_size_request(notebook, -1, prefs_common.compose_notebook_height); gtk_widget_show(notebook); /* header labels and entries */ @@ -7414,8 +7600,8 @@ static Compose *compose_create(PrefsAccount *account, ruler_hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0); - ruler = gtk_shruler_new(); - gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0); + ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL); + gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0); gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE, BORDER_WIDTH); @@ -7427,7 +7613,6 @@ static Compose *compose_create(PrefsAccount *account, gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0); - gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1); text = gtk_text_view_new(); if (prefs_common.show_compose_margin) { @@ -7441,7 +7626,8 @@ static Compose *compose_create(PrefsAccount *account, gtk_text_buffer_add_selection_clipboard(buffer, clipboard); gtk_container_add(GTK_CONTAINER(scrolledwin), text); - + g_signal_connect(G_OBJECT(notebook), "size_allocate", + G_CALLBACK(compose_notebook_size_alloc), compose); g_signal_connect_after(G_OBJECT(text), "size_allocate", G_CALLBACK(compose_edit_size_alloc), ruler); @@ -7475,6 +7661,9 @@ static Compose *compose_create(PrefsAccount *account, g_signal_connect(G_OBJECT(text), "drag-drop", G_CALLBACK(compose_drag_drop), compose); + g_signal_connect(G_OBJECT(text), "key-press-event", + G_CALLBACK(completion_set_focus_to_subject), + compose); gtk_widget_show_all(vbox); /* pane between attach clist and text */ @@ -7648,8 +7837,9 @@ static Compose *compose_create(PrefsAccount *account, gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))), prefs_common_translated_header_name("Newsgroups:")); +#ifndef USE_NEW_ADDRBOOK addressbook_set_target_compose(compose); - +#endif if (mode != COMPOSE_REDIRECT) compose_set_template_menu(compose); else { @@ -7859,7 +8049,7 @@ static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data) cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget)); - if (!GTK_CHECK_MENU_ITEM(widget)->active) + if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return; systemid = g_object_get_data(G_OBJECT(widget), "privacy_system"); @@ -7882,7 +8072,7 @@ static void compose_update_privacy_system_menu_item(Compose * compose, gboolean { static gchar *branch_path = "/Menu/Options/PrivacySystem"; GtkWidget *menuitem = NULL; - GList *amenu; + GList *children, *amenu; gboolean can_sign = FALSE, can_encrypt = FALSE; gboolean found = FALSE; @@ -7892,11 +8082,10 @@ static void compose_update_privacy_system_menu_item(Compose * compose, gboolean gtk_ui_manager_get_widget(compose->ui_manager, branch_path))); cm_return_if_fail(menuitem != NULL); - amenu = GTK_MENU_SHELL(menuitem)->children; + children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem))); + amenu = children; menuitem = NULL; while (amenu != NULL) { - GList *alist = amenu->next; - systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system"); if (systemid != NULL) { if (strcmp(systemid, compose->privacy_system) == 0 && @@ -7918,8 +8107,9 @@ static void compose_update_privacy_system_menu_item(Compose * compose, gboolean break; } - amenu = alist; + amenu = amenu->next; } + g_list_free(children); if (menuitem != NULL) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE); @@ -8325,6 +8515,7 @@ static void compose_template_apply_fields(Compose *compose, Template *tmpl) static void compose_destroy(Compose *compose) { + GtkAllocation allocation; GtkTextBuffer *buffer; GtkClipboard *clipboard; @@ -8339,12 +8530,12 @@ static void compose_destroy(Compose *compose) * however this may change. */ address_completion_end(compose->window); - slist_free_strings(compose->to_list); - g_slist_free(compose->to_list); - slist_free_strings(compose->newsgroup_list); - g_slist_free(compose->newsgroup_list); - slist_free_strings(compose->header_list); - g_slist_free(compose->header_list); + slist_free_strings_full(compose->to_list); + slist_free_strings_full(compose->newsgroup_list); + slist_free_strings_full(compose->header_list); + + slist_free_strings_full(extra_headers); + extra_headers = NULL; compose->header_list = compose->newsgroup_list = compose->to_list = NULL; @@ -8379,9 +8570,10 @@ static void compose_destroy(Compose *compose) g_free(compose->privacy_system); +#ifndef USE_NEW_ADDRBOOK if (addressbook_get_target_compose() == compose) addressbook_set_target_compose(NULL); - +#endif #if USE_ENCHANT if (compose->gtkaspell) { gtkaspell_delete(compose->gtkaspell); @@ -8390,8 +8582,9 @@ static void compose_destroy(Compose *compose) #endif if (!compose->batch) { - prefs_common.compose_width = compose->scrolledwin->allocation.width; - prefs_common.compose_height = compose->window->allocation.height; + gtk_widget_get_allocation(compose->window, &allocation); + prefs_common.compose_width = allocation.width; + prefs_common.compose_height = allocation.height; } if (!gtk_widget_get_parent(compose->paned)) @@ -8405,7 +8598,7 @@ static void compose_destroy(Compose *compose) gtk_widget_destroy(compose->window); toolbar_destroy(compose->toolbar); g_free(compose->toolbar); - g_mutex_free(compose->mutex); + cm_mutex_free(compose->mutex); g_free(compose); } @@ -8534,7 +8727,8 @@ static void compose_attach_property(GtkAction *action, gpointer data) gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE); gtk_widget_grab_focus(attach_prop.ok_btn); gtk_widget_show(attach_prop.window); - manage_window_set_transient(GTK_WINDOW(attach_prop.window)); + gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window), + GTK_WINDOW(compose->window)); optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu); if (ainfo->encoding == ENC_UNKNOWN) @@ -8787,11 +8981,11 @@ static gboolean attach_property_key_pressed(GtkWidget *widget, GdkEventKey *event, gboolean *cancelled) { - if (event && event->keyval == GDK_Escape) { + if (event && event->keyval == GDK_KEY_Escape) { *cancelled = TRUE; gtk_main_quit(); } - if (event && event->keyval == GDK_Return) { + if (event && event->keyval == GDK_KEY_Return) { *cancelled = FALSE; gtk_main_quit(); return TRUE; @@ -8968,7 +9162,7 @@ static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition, Compose *compose = (Compose *)data; gsize bytes_read; - debug_print(_("Compose: input from monitoring process\n")); + debug_print("Compose: input from monitoring process\n"); g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL); @@ -9099,6 +9293,13 @@ static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state, /* callback functions */ +static void compose_notebook_size_alloc(GtkNotebook *notebook, + GtkAllocation *allocation, + Compose *compose) +{ + prefs_common.compose_notebook_height = allocation->height; +} + /* compose_edit_size_alloc() - called when resized. don't know whether Gtk * includes "non-client" (windows-izm) in calculation, so this calculation * may not be accurate. @@ -9117,9 +9318,8 @@ static gboolean compose_edit_size_alloc(GtkEditable *widget, (allocation->width - allocation->x) / char_width; /* got the maximum */ - gtk_ruler_set_range(GTK_RULER(shruler), - 0.0, line_width_in_chars, 0, - /*line_width_in_chars*/ char_width); + gtk_shruler_set_range(GTK_SHRULER(shruler), + 0.0, line_width_in_chars, 0); } return TRUE; @@ -9260,14 +9460,8 @@ static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event, attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection); - if (attach_nr_selected > 0) - { - cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE); - cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE); - } else { - cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE); - cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE); - } + cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0)); + cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected > 0)); gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL, NULL, NULL, event->button, event->time); @@ -9285,7 +9479,7 @@ static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event, if (!event) return FALSE; switch (event->keyval) { - case GDK_Delete: + case GDK_KEY_Delete: compose_attach_remove_selected(NULL, compose); break; } @@ -9380,6 +9574,7 @@ gboolean compose_draft (gpointer data, guint action) Compose *compose = (Compose *)data; FolderItem *draft; gchar *tmp; + gchar *sheaders; gint msgnum; MsgFlags flag = {0, 0}; static gboolean lock = FALSE; @@ -9460,6 +9655,10 @@ gboolean compose_draft (gpointer data, guint action) err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0); err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0); + sheaders = compose_get_manual_headers_info(compose); + err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0); + g_free(sheaders); + /* end of headers */ err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0); @@ -9668,6 +9867,10 @@ static void compose_attach_cb(GtkAction *action, gpointer data) if (compose->redirect_filename != NULL) return; + /* Set focus_window properly, in case we were called via popup menu, + * which unsets it (via focus_out_event callback on compose window). */ + manage_window_focus_in(compose->window, NULL, NULL); + file_list = filesel_select_multiple_files_open(_("Select file")); if (file_list) { @@ -9795,6 +9998,15 @@ static void compose_close_cb(GtkAction *action, gpointer data) compose_close(compose); } +static void compose_print_cb(GtkAction *action, gpointer data) +{ + Compose *compose = (Compose *) data; + + compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE); + if (compose->targetinfo) + messageview_print(compose->targetinfo, FALSE, -1, -1, 0); +} + static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data) { gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current)); @@ -9809,7 +10021,17 @@ static void compose_address_cb(GtkAction *action, gpointer data) { Compose *compose = (Compose *)data; +#ifndef USE_NEW_ADDRBOOK addressbook_open(compose); +#else + GError* error = NULL; + addressbook_connect_signals(compose); + addressbook_dbus_open(TRUE, &error); + if (error) { + g_warning("%s", error->message); + g_error_free(error); + } +#endif } static void about_show_cb(GtkAction *action, gpointer data) @@ -9965,7 +10187,7 @@ static void compose_cut_cb(GtkAction *action, gpointer data) Compose *compose = (Compose *)data; if (compose->focused_editable #ifndef GENERIC_UMPC - && gtkut_widget_has_focus(compose->focused_editable) + && gtk_widget_has_focus(compose->focused_editable) #endif ) entry_cut_clipboard(compose->focused_editable); @@ -9976,7 +10198,7 @@ static void compose_copy_cb(GtkAction *action, gpointer data) Compose *compose = (Compose *)data; if (compose->focused_editable #ifndef GENERIC_UMPC - && gtkut_widget_has_focus(compose->focused_editable) + && gtk_widget_has_focus(compose->focused_editable) #endif ) entry_copy_clipboard(compose->focused_editable); @@ -9989,14 +10211,20 @@ static void compose_paste_cb(GtkAction *action, gpointer data) GtkTextBuffer *buffer; BLOCK_WRAP(); if (compose->focused_editable && - gtkut_widget_has_focus(compose->focused_editable)) +#ifndef GENERIC_UMPC + gtk_widget_has_focus(compose->focused_editable) +#endif + ) entry_paste_clipboard(compose, compose->focused_editable, prefs_common.linewrap_pastes, GDK_SELECTION_CLIPBOARD, NULL); UNBLOCK_WRAP(); #ifdef USE_ENCHANT - if (gtkut_widget_has_focus(compose->text) && + if ( +#ifndef GENERIC_UMPC + gtk_widget_has_focus(compose->text) && +#endif compose->gtkaspell && compose->gtkaspell->check_while_typing) gtkaspell_highlight_all(compose->gtkaspell); @@ -10009,7 +10237,7 @@ static void compose_paste_as_quote_cb(GtkAction *action, gpointer data) gint wrap_quote = prefs_common.linewrap_quote; if (compose->focused_editable #ifndef GENERIC_UMPC - && gtkut_widget_has_focus(compose->focused_editable) + && gtk_widget_has_focus(compose->focused_editable) #endif ) { /* let text_insert() (called directly or at a later time @@ -10038,7 +10266,7 @@ static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data) BLOCK_WRAP(); if (compose->focused_editable #ifndef GENERIC_UMPC - && gtkut_widget_has_focus(compose->focused_editable) + && gtk_widget_has_focus(compose->focused_editable) #endif ) entry_paste_clipboard(compose, compose->focused_editable, FALSE, @@ -10046,7 +10274,10 @@ static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data) UNBLOCK_WRAP(); #ifdef USE_ENCHANT - if (gtkut_widget_has_focus(compose->text) && + if ( +#ifndef GENERIC_UMPC + gtk_widget_has_focus(compose->text) && +#endif compose->gtkaspell && compose->gtkaspell->check_while_typing) gtkaspell_highlight_all(compose->gtkaspell); @@ -10061,7 +10292,7 @@ static void compose_paste_wrap_cb(GtkAction *action, gpointer data) BLOCK_WRAP(); if (compose->focused_editable #ifndef GENERIC_UMPC - && gtkut_widget_has_focus(compose->focused_editable) + && gtk_widget_has_focus(compose->focused_editable) #endif ) entry_paste_clipboard(compose, compose->focused_editable, TRUE, @@ -10069,7 +10300,10 @@ static void compose_paste_wrap_cb(GtkAction *action, gpointer data) UNBLOCK_WRAP(); #ifdef USE_ENCHANT - if (gtkut_widget_has_focus(compose->text) && + if ( +#ifndef GENERIC_UMPC + gtk_widget_has_focus(compose->text) && +#endif compose->gtkaspell && compose->gtkaspell->check_while_typing) gtkaspell_highlight_all(compose->gtkaspell); @@ -10081,7 +10315,7 @@ static void compose_allsel_cb(GtkAction *action, gpointer data) Compose *compose = (Compose *)data; if (compose->focused_editable #ifndef GENERIC_UMPC - && gtkut_widget_has_focus(compose->focused_editable) + && gtk_widget_has_focus(compose->focused_editable) #endif ) entry_allsel(compose->focused_editable); @@ -10156,14 +10390,12 @@ static void textview_move_backward_word (GtkTextView *text) GtkTextBuffer *buffer; GtkTextMark *mark; GtkTextIter ins; - gint count; cm_return_if_fail(GTK_IS_TEXT_VIEW(text)); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); mark = gtk_text_buffer_get_insert(buffer); gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark); - count = gtk_text_iter_inside_word (&ins) ? 2 : 1; if (gtk_text_iter_backward_word_starts(&ins, 1)) gtk_text_buffer_place_cursor(buffer, &ins); } @@ -10385,7 +10617,7 @@ static void compose_advanced_action_cb(GtkAction *gaction, gpointer data) {textview_delete_to_line_end} }; - if (!gtkut_widget_has_focus(GTK_WIDGET(text))) return; + if (!gtk_widget_has_focus(GTK_WIDGET(text))) return; if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE && action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) { @@ -10398,6 +10630,8 @@ static void compose_advanced_action_cb(GtkAction *gaction, gpointer data) static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose) { + GtkAllocation allocation; + GtkWidget *parent; gchar *str = NULL; if (GTK_IS_EDITABLE(widget)) { @@ -10405,24 +10639,29 @@ static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose) gtk_editable_set_position(GTK_EDITABLE(widget), strlen(str)); g_free(str); - if (widget->parent && widget->parent->parent - && widget->parent->parent->parent) { - if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) { - gint y = widget->allocation.y; - gint height = widget->allocation.height; + if ((parent = gtk_widget_get_parent(widget)) + && (parent = gtk_widget_get_parent(parent)) + && (parent = gtk_widget_get_parent(parent))) { + if (GTK_IS_SCROLLED_WINDOW(parent)) { + gtk_widget_get_allocation(widget, &allocation); + gint y = allocation.y; + gint height = allocation.height; GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment - (GTK_SCROLLED_WINDOW(widget->parent->parent->parent)); + (GTK_SCROLLED_WINDOW(parent)); - if (y < (int)shown->value) { - gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1); + gfloat value = gtk_adjustment_get_value(shown); + gfloat upper = gtk_adjustment_get_upper(shown); + gfloat page_size = gtk_adjustment_get_page_size(shown); + if (y < (int)value) { + gtk_adjustment_set_value(shown, y - 1); } - if (y + height > (int)shown->value + (int)shown->page_size) { - if (y - height - 1 < (int)shown->upper - (int)shown->page_size) { - gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), - y + height - (int)shown->page_size - 1); + if ((y + height) > ((int)value + (int)page_size)) { + if ((y - height - 1) < ((int)upper - (int)page_size)) { + gtk_adjustment_set_value(shown, + y + height - (int)page_size - 1); } else { - gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), - (int)shown->upper - (int)shown->page_size - 1); + gtk_adjustment_set_value(shown, + (int)upper - (int)page_size - 1); } } } @@ -10563,14 +10802,17 @@ static void compose_attach_drag_received_cb (GtkWidget *widget, { Compose *compose = (Compose *)user_data; GList *list, *tmp; + GdkAtom type; - if (((gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) + type = gtk_selection_data_get_data_type(data); + if (((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) #ifdef G_OS_WIN32 - || (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) + || (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) #endif ) && gtk_drag_get_source_widget(context) != summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) { - list = uri_list_extract_filenames((const gchar *)data->data); + list = uri_list_extract_filenames( + (const gchar *)gtk_selection_data_get_data(data)); for (tmp = list; tmp != NULL; tmp = tmp->next) { gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data); compose_attach_append @@ -10619,6 +10861,21 @@ static gboolean compose_drag_drop(GtkWidget *widget, return TRUE; } +static gboolean completion_set_focus_to_subject + (GtkWidget *widget, + GdkEventKey *event, + Compose *compose) +{ + cm_return_val_if_fail(compose != NULL, FALSE); + + /* make backtab move to subject field */ + if(event->keyval == GDK_KEY_ISO_Left_Tab) { + gtk_widget_grab_focus(compose->subject_entry); + return TRUE; + } + return FALSE; +} + static void compose_insert_drag_received_cb (GtkWidget *widget, GdkDragContext *drag_context, gint x, @@ -10630,20 +10887,23 @@ static void compose_insert_drag_received_cb (GtkWidget *widget, { Compose *compose = (Compose *)user_data; GList *list, *tmp; + GdkAtom type; /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE) * does not work */ + type = gtk_selection_data_get_data_type(data); #ifndef G_OS_WIN32 - if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) { + if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) { #else - if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "DROPFILES_DND")) { + if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "DROPFILES_DND")) { #endif AlertValue val = G_ALERTDEFAULT; + const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data); - list = uri_list_extract_filenames((const gchar *)data->data); - if (list == NULL && strstr((gchar *)(data->data), "://")) { + list = uri_list_extract_filenames(ddata); + if (list == NULL && strstr(ddata, "://")) { /* Assume a list of no files, and data has ://, is a remote link */ - gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data)); + gchar *tmpdata = g_strstrip(g_strdup(ddata)); gchar *tmpfile = get_tmp_file(); str_write_to_file(tmpdata, tmpfile); g_free(tmpdata); @@ -10717,7 +10977,7 @@ static void compose_header_drag_received_cb (GtkWidget *widget, gpointer user_data) { GtkEditable *entry = (GtkEditable *)user_data; - gchar *email = (gchar *)data->data; + const gchar *email = (const gchar *)gtk_selection_data_get_data(data); /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE) * does not work */ @@ -10726,8 +10986,7 @@ static void compose_header_drag_received_cb (GtkWidget *widget, gchar *decoded=g_new(gchar, strlen(email)); int start = 0; - email += strlen("mailto:"); - decode_uri(decoded, email); /* will fit */ + decode_uri(decoded, email + strlen("mailto:")); /* will fit */ gtk_editable_delete_text(entry, 0, -1); gtk_editable_insert_text(entry, decoded, strlen(decoded), &start); gtk_drag_finish(drag_context, TRUE, FALSE, time); @@ -10771,7 +11030,7 @@ static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry, if ((g_slist_length(headerentry->compose->header_list) > 0) && ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) && !(event->state & GDK_MODIFIER_MASK) && - (event->keyval == GDK_BackSpace) && + (event->keyval == GDK_KEY_BackSpace) && (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) { gtk_container_remove (GTK_CONTAINER(headerentry->compose->header_table), @@ -10783,7 +11042,7 @@ static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry, g_slist_remove(headerentry->compose->header_list, headerentry); g_free(headerentry); - } else if (event->keyval == GDK_Tab) { + } else if (event->keyval == GDK_KEY_Tab) { if (headerentry->compose->header_last == headerentry) { /* Override default next focus, and give it to subject_entry * instead of notebook tabs @@ -10799,6 +11058,9 @@ static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry, static gboolean scroll_postpone(gpointer data) { Compose *compose = (Compose *)data; + + cm_return_val_if_fail(!compose->batch, FALSE); + GTK_EVENTS_FLUSH(); compose_show_first_last_header(compose, FALSE); return FALSE; @@ -10812,20 +11074,33 @@ static void compose_headerentry_changed_cb(GtkWidget *entry, g_signal_handlers_disconnect_matched (G_OBJECT(entry), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, headerentry); - - g_timeout_add(0, scroll_postpone, headerentry->compose); + + if (!headerentry->compose->batch) + g_timeout_add(0, scroll_postpone, headerentry->compose); } } +static gboolean compose_defer_auto_save_draft(Compose *compose) +{ + compose->draft_timeout_tag = -1; + compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE); + return FALSE; +} + static void compose_show_first_last_header(Compose *compose, gboolean show_first) { GtkAdjustment *vadj; cm_return_if_fail(compose); + cm_return_if_fail(!compose->batch); cm_return_if_fail(GTK_IS_WIDGET(compose->header_table)); - cm_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent)); - vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent)); - gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : (vadj->upper - vadj->page_size))); + cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table))); + vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT( + gtk_widget_get_parent(compose->header_table))); + gtk_adjustment_set_value(vadj, (show_first ? + gtk_adjustment_get_lower(vadj) : + (gtk_adjustment_get_upper(vadj) - + gtk_adjustment_get_page_size(vadj)))); gtk_adjustment_changed(vadj); } @@ -10930,13 +11205,7 @@ static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter, gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 && compose->draft_timeout_tag != -2 /* disabled while loading */) compose->draft_timeout_tag = g_timeout_add - (500, (GtkFunction) compose_defer_auto_save_draft, compose); -} -static gint compose_defer_auto_save_draft(Compose *compose) -{ - compose->draft_timeout_tag = -1; - compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE); - return FALSE; + (500, (GSourceFunc) compose_defer_auto_save_draft, compose); } #if USE_ENCHANT @@ -10946,7 +11215,7 @@ static void compose_check_all(GtkAction *action, gpointer data) if (!compose->gtkaspell) return; - if (gtkut_widget_has_focus(compose->subject_entry)) + if (gtk_widget_has_focus(compose->subject_entry)) claws_spell_entry_check_all( CLAWS_SPELL_ENTRY(compose->subject_entry)); else @@ -10971,7 +11240,7 @@ static void compose_check_backwards(GtkAction *action, gpointer data) return; } - if (gtkut_widget_has_focus(compose->subject_entry)) + if (gtk_widget_has_focus(compose->subject_entry)) claws_spell_entry_check_backwards( CLAWS_SPELL_ENTRY(compose->subject_entry)); else @@ -10986,7 +11255,7 @@ static void compose_check_forwards_go(GtkAction *action, gpointer data) return; } - if (gtkut_widget_has_focus(compose->subject_entry)) + if (gtk_widget_has_focus(compose->subject_entry)) claws_spell_entry_check_forwards_go( CLAWS_SPELL_ENTRY(compose->subject_entry)); else