Make last colors configurable using hidden prefs: tags bg/fg colors in
[claws.git] / src / compose.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2016 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #  include "config.h"
21 #include "claws-features.h"
22 #endif
23
24 #include "defs.h"
25
26 #ifndef PANGO_ENABLE_ENGINE
27 #  define PANGO_ENABLE_ENGINE
28 #endif
29
30 #include <glib.h>
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <gtk/gtk.h>
34
35 #include <pango/pango-break.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <time.h>
44 #include <stdlib.h>
45 #if HAVE_SYS_WAIT_H
46 #  include <sys/wait.h>
47 #endif
48 #include <signal.h>
49 #include <errno.h>
50 #ifndef G_OS_WIN32  /* fixme we should have a configure test. */
51 #include <libgen.h>
52 #endif
53
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
55 #  include <wchar.h>
56 #  include <wctype.h>
57 #endif
58
59 #include "claws.h"
60 #include "main.h"
61 #include "mainwindow.h"
62 #include "compose.h"
63 #ifndef USE_ALT_ADDRBOOK
64         #include "addressbook.h"
65 #else
66         #include "addressbook-dbus.h"
67         #include "addressadd.h"
68 #endif
69 #include "folderview.h"
70 #include "procmsg.h"
71 #include "menu.h"
72 #include "stock_pixmap.h"
73 #include "send_message.h"
74 #include "imap.h"
75 #include "news.h"
76 #include "customheader.h"
77 #include "prefs_common.h"
78 #include "prefs_account.h"
79 #include "action.h"
80 #include "account.h"
81 #include "filesel.h"
82 #include "procheader.h"
83 #include "procmime.h"
84 #include "statusbar.h"
85 #include "about.h"
86 #include "quoted-printable.h"
87 #include "codeconv.h"
88 #include "utils.h"
89 #include "gtkutils.h"
90 #include "gtkshruler.h"
91 #include "socket.h"
92 #include "alertpanel.h"
93 #include "manage_window.h"
94 #include "folder.h"
95 #include "folder_item_prefs.h"
96 #include "addr_compl.h"
97 #include "quote_fmt.h"
98 #include "undo.h"
99 #include "foldersel.h"
100 #include "toolbar.h"
101 #include "inc.h"
102 #include "message_search.h"
103 #include "combobox.h"
104 #include "hooks.h"
105 #include "privacy.h"
106 #include "timing.h"
107 #include "autofaces.h"
108 #include "spell_entry.h"
109
110 enum
111 {
112         COL_MIMETYPE = 0,
113         COL_SIZE     = 1,
114         COL_NAME     = 2,
115         COL_CHARSET  = 3,
116         COL_DATA     = 4,
117         COL_AUTODATA = 5,
118         N_COL_COLUMNS
119 };
120
121 #define N_ATTACH_COLS   (N_COL_COLUMNS)
122
123 typedef enum
124 {
125         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
126         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
127         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
128         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
129         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
130         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
131         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
132         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
133         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
134         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
135         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
136         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
137         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
138         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
139 } ComposeCallAdvancedAction;
140
141 typedef enum
142 {
143         PRIORITY_HIGHEST = 1,
144         PRIORITY_HIGH,
145         PRIORITY_NORMAL,
146         PRIORITY_LOW,
147         PRIORITY_LOWEST
148 } PriorityLevel;
149
150 typedef enum
151 {
152         COMPOSE_INSERT_SUCCESS,
153         COMPOSE_INSERT_READ_ERROR,
154         COMPOSE_INSERT_INVALID_CHARACTER,
155         COMPOSE_INSERT_NO_FILE
156 } ComposeInsertResult;
157
158 typedef enum
159 {
160         COMPOSE_WRITE_FOR_SEND,
161         COMPOSE_WRITE_FOR_STORE
162 } ComposeWriteType;
163
164 typedef enum
165 {
166         COMPOSE_QUOTE_FORCED,
167         COMPOSE_QUOTE_CHECK,
168         COMPOSE_QUOTE_SKIP
169 } ComposeQuoteMode;
170
171 typedef enum {
172     TO_FIELD_PRESENT,
173     SUBJECT_FIELD_PRESENT,
174     BODY_FIELD_PRESENT,
175     NO_FIELD_PRESENT
176 } MailField;
177
178 #define B64_LINE_SIZE           57
179 #define B64_BUFFSIZE            77
180
181 #define MAX_REFERENCES_LEN      999
182
183 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
184 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
185
186 static GdkColor default_to_bgcolor = {
187         (gulong)0,
188         (gushort)0,
189         (gushort)0,
190         (gushort)0
191 };
192
193 static GdkColor default_to_color = {
194         (gulong)0,
195         (gushort)0,
196         (gushort)0,
197         (gushort)0
198 };
199
200 static GList *compose_list = NULL;
201 static GSList *extra_headers = NULL;
202
203 static Compose *compose_generic_new                     (PrefsAccount   *account,
204                                                  const gchar    *to,
205                                                  FolderItem     *item,
206                                                  GList          *attach_files,
207                                                  GList          *listAddress );
208
209 static Compose *compose_create                  (PrefsAccount   *account,
210                                                  FolderItem              *item,
211                                                  ComposeMode     mode,
212                                                  gboolean batch);
213
214 static void compose_entry_mark_default_to       (Compose          *compose,
215                                          const gchar      *address);
216 static Compose *compose_followup_and_reply_to   (MsgInfo        *msginfo,
217                                          ComposeQuoteMode        quote_mode,
218                                          gboolean        to_all,
219                                          gboolean        to_sender,
220                                          const gchar    *body);
221 static Compose *compose_forward_multiple        (PrefsAccount   *account, 
222                                          GSList         *msginfo_list);
223 static Compose *compose_reply                   (MsgInfo        *msginfo,
224                                          ComposeQuoteMode        quote_mode,
225                                          gboolean        to_all,
226                                          gboolean        to_ml,
227                                          gboolean        to_sender,
228                                          const gchar    *body);
229 static Compose *compose_reply_mode              (ComposeMode     mode, 
230                                          GSList         *msginfo_list, 
231                                          gchar          *body);
232 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
233 static void compose_update_privacy_systems_menu(Compose *compose);
234
235 static GtkWidget *compose_account_option_menu_create
236                                                 (Compose        *compose);
237 static void compose_set_out_encoding            (Compose        *compose);
238 static void compose_set_template_menu           (Compose        *compose);
239 static void compose_destroy                     (Compose        *compose);
240
241 static MailField compose_entries_set            (Compose        *compose,
242                                                  const gchar    *mailto,
243                                                  ComposeEntryType to_type);
244 static gint compose_parse_header                (Compose        *compose,
245                                                  MsgInfo        *msginfo);
246 static gint compose_parse_manual_headers        (Compose        *compose,
247                                                  MsgInfo        *msginfo,
248                                                  HeaderEntry    *entries);
249 static gchar *compose_parse_references          (const gchar    *ref,
250                                                  const gchar    *msgid);
251
252 static gchar *compose_quote_fmt                 (Compose        *compose,
253                                                  MsgInfo        *msginfo,
254                                                  const gchar    *fmt,
255                                                  const gchar    *qmark,
256                                                  const gchar    *body,
257                                                  gboolean        rewrap,
258                                                  gboolean        need_unescape,
259                                                  const gchar *err_msg);
260
261 static void compose_reply_set_entry             (Compose        *compose,
262                                                  MsgInfo        *msginfo,
263                                                  gboolean        to_all,
264                                                  gboolean        to_ml,
265                                                  gboolean        to_sender,
266                                                  gboolean
267                                                  followup_and_reply_to);
268 static void compose_reedit_set_entry            (Compose        *compose,
269                                                  MsgInfo        *msginfo);
270
271 static void compose_insert_sig                  (Compose        *compose,
272                                                  gboolean        replace);
273 static ComposeInsertResult compose_insert_file  (Compose        *compose,
274                                                  const gchar    *file);
275
276 static gboolean compose_attach_append           (Compose        *compose,
277                                                  const gchar    *file,
278                                                  const gchar    *type,
279                                                  const gchar    *content_type,
280                                                  const gchar    *charset);
281 static void compose_attach_parts                (Compose        *compose,
282                                                  MsgInfo        *msginfo);
283
284 static gboolean compose_beautify_paragraph      (Compose        *compose,
285                                                  GtkTextIter    *par_iter,
286                                                  gboolean        force);
287 static void compose_wrap_all                    (Compose        *compose);
288 static void compose_wrap_all_full               (Compose        *compose,
289                                                  gboolean        autowrap);
290
291 static void compose_set_title                   (Compose        *compose);
292 static void compose_select_account              (Compose        *compose,
293                                                  PrefsAccount   *account,
294                                                  gboolean        init);
295
296 static PrefsAccount *compose_current_mail_account(void);
297 /* static gint compose_send                     (Compose        *compose); */
298 static gboolean compose_check_for_valid_recipient
299                                                 (Compose        *compose);
300 static gboolean compose_check_entries           (Compose        *compose,
301                                                  gboolean       check_everything);
302 static gint compose_write_to_file               (Compose        *compose,
303                                                  FILE           *fp,
304                                                  gint            action,
305                                                  gboolean        attach_parts);
306 static gint compose_write_body_to_file          (Compose        *compose,
307                                                  const gchar    *file);
308 static gint compose_remove_reedit_target        (Compose        *compose,
309                                                  gboolean        force);
310 static void compose_remove_draft                        (Compose        *compose);
311 static gint compose_queue_sub                   (Compose        *compose,
312                                                  gint           *msgnum,
313                                                  FolderItem     **item,
314                                                  gchar          **msgpath,
315                                                  gboolean       check_subject,
316                                                  gboolean       remove_reedit_target);
317 static int compose_add_attachments              (Compose        *compose,
318                                                  MimeInfo       *parent);
319 static gchar *compose_get_header                (Compose        *compose);
320 static gchar *compose_get_manual_headers_info   (Compose        *compose);
321
322 static void compose_convert_header              (Compose        *compose,
323                                                  gchar          *dest,
324                                                  gint            len,
325                                                  gchar          *src,
326                                                  gint            header_len,
327                                                  gboolean        addr_field);
328
329 static void compose_attach_info_free            (AttachInfo     *ainfo);
330 static void compose_attach_remove_selected      (GtkAction      *action,
331                                                  gpointer        data);
332
333 static void compose_template_apply              (Compose        *compose,
334                                                  Template       *tmpl,
335                                                  gboolean        replace);
336 static void compose_attach_property             (GtkAction      *action,
337                                                  gpointer        data);
338 static void compose_attach_property_create      (gboolean       *cancelled);
339 static void attach_property_ok                  (GtkWidget      *widget,
340                                                  gboolean       *cancelled);
341 static void attach_property_cancel              (GtkWidget      *widget,
342                                                  gboolean       *cancelled);
343 static gint attach_property_delete_event        (GtkWidget      *widget,
344                                                  GdkEventAny    *event,
345                                                  gboolean       *cancelled);
346 static gboolean attach_property_key_pressed     (GtkWidget      *widget,
347                                                  GdkEventKey    *event,
348                                                  gboolean       *cancelled);
349
350 static void compose_exec_ext_editor             (Compose        *compose);
351 #ifdef G_OS_UNIX
352 static gint compose_exec_ext_editor_real        (const gchar    *file,
353                                                  GdkNativeWindow socket_wid);
354 static gboolean compose_ext_editor_kill         (Compose        *compose);
355 static gboolean compose_input_cb                (GIOChannel     *source,
356                                                  GIOCondition    condition,
357                                                  gpointer        data);
358 static void compose_set_ext_editor_sensitive    (Compose        *compose,
359                                                  gboolean        sensitive);
360 static gboolean compose_get_ext_editor_cmd_valid();
361 static gboolean compose_get_ext_editor_uses_socket();
362 static gboolean compose_ext_editor_plug_removed_cb
363                                                 (GtkSocket      *socket,
364                                                  Compose        *compose);
365 #endif /* G_OS_UNIX */
366
367 static void compose_undo_state_changed          (UndoMain       *undostruct,
368                                                  gint            undo_state,
369                                                  gint            redo_state,
370                                                  gpointer        data);
371
372 static void compose_create_header_entry (Compose *compose);
373 static void compose_add_header_entry    (Compose *compose, const gchar *header,
374                                          gchar *text, ComposePrefType pref_type);
375 static void compose_remove_header_entries(Compose *compose);
376
377 static void compose_update_priority_menu_item(Compose * compose);
378 #if USE_ENCHANT
379 static void compose_spell_menu_changed  (void *data);
380 static void compose_dict_changed        (void *data);
381 #endif
382 static void compose_add_field_list      ( Compose *compose,
383                                           GList *listAddress );
384
385 /* callback functions */
386
387 static void compose_notebook_size_alloc (GtkNotebook *notebook,
388                                          GtkAllocation *allocation,
389                                          GtkPaned *paned);
390 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
391                                          GtkAllocation  *allocation,
392                                          GtkSHRuler     *shruler);
393 static void account_activated           (GtkComboBox *optmenu,
394                                          gpointer        data);
395 static void attach_selected             (GtkTreeView    *tree_view, 
396                                          GtkTreePath    *tree_path,
397                                          GtkTreeViewColumn *column, 
398                                          Compose *compose);
399 static gboolean attach_button_pressed   (GtkWidget      *widget,
400                                          GdkEventButton *event,
401                                          gpointer        data);
402 static gboolean attach_key_pressed      (GtkWidget      *widget,
403                                          GdkEventKey    *event,
404                                          gpointer        data);
405 static void compose_send_cb             (GtkAction      *action, gpointer data);
406 static void compose_send_later_cb       (GtkAction      *action, gpointer data);
407
408 static void compose_save_cb             (GtkAction      *action,
409                                          gpointer        data);
410
411 static void compose_attach_cb           (GtkAction      *action,
412                                          gpointer        data);
413 static void compose_insert_file_cb      (GtkAction      *action,
414                                          gpointer        data);
415 static void compose_insert_sig_cb       (GtkAction      *action,
416                                          gpointer        data);
417 static void compose_replace_sig_cb      (GtkAction      *action,
418                                          gpointer        data);
419
420 static void compose_close_cb            (GtkAction      *action,
421                                          gpointer        data);
422 static void compose_print_cb            (GtkAction      *action,
423                                          gpointer        data);
424
425 static void compose_set_encoding_cb     (GtkAction      *action, GtkRadioAction *current, gpointer data);
426
427 static void compose_address_cb          (GtkAction      *action,
428                                          gpointer        data);
429 static void about_show_cb               (GtkAction      *action,
430                                          gpointer        data);
431 static void compose_template_activate_cb(GtkWidget      *widget,
432                                          gpointer        data);
433
434 static void compose_ext_editor_cb       (GtkAction      *action,
435                                          gpointer        data);
436
437 static gint compose_delete_cb           (GtkWidget      *widget,
438                                          GdkEventAny    *event,
439                                          gpointer        data);
440
441 static void compose_undo_cb             (GtkAction      *action,
442                                          gpointer        data);
443 static void compose_redo_cb             (GtkAction      *action,
444                                          gpointer        data);
445 static void compose_cut_cb              (GtkAction      *action,
446                                          gpointer        data);
447 static void compose_copy_cb             (GtkAction      *action,
448                                          gpointer        data);
449 static void compose_paste_cb            (GtkAction      *action,
450                                          gpointer        data);
451 static void compose_paste_as_quote_cb   (GtkAction      *action,
452                                          gpointer        data);
453 static void compose_paste_no_wrap_cb    (GtkAction      *action,
454                                          gpointer        data);
455 static void compose_paste_wrap_cb       (GtkAction      *action,
456                                          gpointer        data);
457 static void compose_allsel_cb           (GtkAction      *action,
458                                          gpointer        data);
459
460 static void compose_advanced_action_cb  (GtkAction      *action,
461                                          gpointer        data);
462
463 static void compose_grab_focus_cb       (GtkWidget      *widget,
464                                          Compose        *compose);
465
466 static void compose_changed_cb          (GtkTextBuffer  *textbuf,
467                                          Compose        *compose);
468
469 static void compose_wrap_cb             (GtkAction      *action,
470                                          gpointer        data);
471 static void compose_wrap_all_cb         (GtkAction      *action,
472                                          gpointer        data);
473 static void compose_find_cb             (GtkAction      *action,
474                                          gpointer        data);
475 static void compose_toggle_autowrap_cb  (GtkToggleAction *action,
476                                          gpointer        data);
477 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
478                                          gpointer        data);
479
480 static void compose_toggle_ruler_cb     (GtkToggleAction *action,
481                                          gpointer        data);
482 static void compose_toggle_sign_cb      (GtkToggleAction *action,
483                                          gpointer        data);
484 static void compose_toggle_encrypt_cb   (GtkToggleAction *action,
485                                          gpointer        data);
486 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
487 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
488 static void activate_privacy_system     (Compose *compose, 
489                                          PrefsAccount *account,
490                                          gboolean warn);
491 static void compose_use_signing(Compose *compose, gboolean use_signing);
492 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
493 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
494                                          gpointer        data);
495 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
496                                          gpointer        data);
497 static void compose_set_priority_cb     (GtkAction *action, GtkRadioAction *current, gpointer data);
498 static void compose_reply_change_mode   (Compose *compose, ComposeMode action);
499 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
500
501 static void compose_attach_drag_received_cb (GtkWidget          *widget,
502                                              GdkDragContext     *drag_context,
503                                              gint                x,
504                                              gint                y,
505                                              GtkSelectionData   *data,
506                                              guint               info,
507                                              guint               time,
508                                              gpointer            user_data);
509 static void compose_insert_drag_received_cb (GtkWidget          *widget,
510                                              GdkDragContext     *drag_context,
511                                              gint                x,
512                                              gint                y,
513                                              GtkSelectionData   *data,
514                                              guint               info,
515                                              guint               time,
516                                              gpointer            user_data);
517 static void compose_header_drag_received_cb (GtkWidget          *widget,
518                                              GdkDragContext     *drag_context,
519                                              gint                x,
520                                              gint                y,
521                                              GtkSelectionData   *data,
522                                              guint               info,
523                                              guint               time,
524                                              gpointer            user_data);
525
526 static gboolean compose_drag_drop           (GtkWidget *widget,
527                                              GdkDragContext *drag_context,
528                                              gint x, gint y,
529                                              guint time, gpointer user_data);
530 static gboolean completion_set_focus_to_subject
531                                         (GtkWidget    *widget,
532                                          GdkEventKey  *event,
533                                          Compose      *user_data);
534
535 static void text_inserted               (GtkTextBuffer  *buffer,
536                                          GtkTextIter    *iter,
537                                          const gchar    *text,
538                                          gint            len,
539                                          Compose        *compose);
540 static Compose *compose_generic_reply(MsgInfo *msginfo,
541                                   ComposeQuoteMode quote_mode,
542                                   gboolean to_all,
543                                   gboolean to_ml,
544                                   gboolean to_sender,
545                                   gboolean followup_and_reply_to,
546                                   const gchar *body);
547
548 static void compose_headerentry_changed_cb         (GtkWidget          *entry,
549                                             ComposeHeaderEntry *headerentry);
550 static gboolean compose_headerentry_key_press_event_cb(GtkWidget               *entry,
551                                             GdkEventKey        *event,
552                                             ComposeHeaderEntry *headerentry);
553 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
554                                         ComposeHeaderEntry *headerentry);
555
556 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
557
558 static void compose_allow_user_actions (Compose *compose, gboolean allow);
559
560 static void compose_nothing_cb             (GtkAction *action, gpointer data)
561 {
562
563 }
564
565 #if USE_ENCHANT
566 static void compose_check_all              (GtkAction *action, gpointer data);
567 static void compose_highlight_all          (GtkAction *action, gpointer data);
568 static void compose_check_backwards        (GtkAction *action, gpointer data);
569 static void compose_check_forwards_go      (GtkAction *action, gpointer data);
570 #endif
571
572 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
573
574 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
575
576 #ifdef USE_ENCHANT
577 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
578                                                 FolderItem *folder_item);
579 #endif
580 static void compose_attach_update_label(Compose *compose);
581 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
582                                      gboolean respect_default_to);
583 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
584 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
585
586 static GtkActionEntry compose_popup_entries[] =
587 {
588         {"Compose",                     NULL, "Compose" },
589         {"Compose/Add",                 NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
590         {"Compose/Remove",                      NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
591         {"Compose/---",                 NULL, "---", NULL, NULL, NULL },
592         {"Compose/Properties",          NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
593 };
594
595 static GtkActionEntry compose_entries[] =
596 {
597         {"Menu",                                NULL, "Menu" },
598 /* menus */
599         {"Message",                     NULL, N_("_Message") },
600         {"Edit",                        NULL, N_("_Edit") },
601 #if USE_ENCHANT
602         {"Spelling",                    NULL, N_("_Spelling") },
603 #endif
604         {"Options",                     NULL, N_("_Options") },
605         {"Tools",                       NULL, N_("_Tools") },
606         {"Help",                        NULL, N_("_Help") },
607 /* Message menu */
608         {"Message/Send",                NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
609         {"Message/SendLater",           NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
610         {"Message/---",                 NULL, "---" },
611
612         {"Message/AttachFile",          NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
613         {"Message/InsertFile",          NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
614         {"Message/InsertSig",           NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
615         {"Message/ReplaceSig",          NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
616         /* {"Message/---",              NULL, "---" }, */
617         {"Message/Save",                NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
618         /* {"Message/---",              NULL, "---" }, */
619         {"Message/Print",               NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
620         /* {"Message/---",              NULL, "---" }, */
621         {"Message/Close",               NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
622
623 /* Edit menu */
624         {"Edit/Undo",                   NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
625         {"Edit/Redo",                   NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
626         {"Edit/---",                    NULL, "---" },
627
628         {"Edit/Cut",                    NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
629         {"Edit/Copy",                   NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
630         {"Edit/Paste",                  NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
631
632         {"Edit/SpecialPaste",           NULL, N_("_Special paste") },
633         {"Edit/SpecialPaste/AsQuotation",       NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
634         {"Edit/SpecialPaste/Wrapped",   NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
635         {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
636
637         {"Edit/SelectAll",              NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
638
639         {"Edit/Advanced",               NULL, N_("A_dvanced") },
640         {"Edit/Advanced/BackChar",      NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
641         {"Edit/Advanced/ForwChar",      NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
642         {"Edit/Advanced/BackWord",      NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
643         {"Edit/Advanced/ForwWord",      NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
644         {"Edit/Advanced/BegLine",       NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
645         {"Edit/Advanced/EndLine",       NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
646         {"Edit/Advanced/PrevLine",      NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
647         {"Edit/Advanced/NextLine",      NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
648         {"Edit/Advanced/DelBackChar",   NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
649         {"Edit/Advanced/DelForwChar",   NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
650         {"Edit/Advanced/DelBackWord",   NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
651         {"Edit/Advanced/DelForwWord",   NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
652         {"Edit/Advanced/DelLine",       NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
653         {"Edit/Advanced/DelEndLine",    NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
654
655         /* {"Edit/---",                 NULL, "---" }, */
656         {"Edit/Find",           NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
657
658         /* {"Edit/---",                 NULL, "---" }, */
659         {"Edit/WrapPara",               NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
660         {"Edit/WrapAllLines",           NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
661         /* {"Edit/---",                 NULL, "---" }, */
662         {"Edit/ExtEditor",              NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
663 #if USE_ENCHANT
664 /* Spelling menu */
665         {"Spelling/CheckAllSel",        NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
666         {"Spelling/HighlightAll",       NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
667         {"Spelling/CheckBackwards",     NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
668         {"Spelling/ForwardNext",        NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
669
670         {"Spelling/---",                NULL, "---" },
671         {"Spelling/Options",            NULL, N_("_Options") },
672 #endif
673
674 /* Options menu */
675
676         {"Options/ReplyMode",           NULL, N_("Reply _mode") },
677         {"Options/---",                 NULL, "---" },
678         {"Options/PrivacySystem",       NULL, N_("Privacy _System") },
679         {"Options/PrivacySystem/PlaceHolder",   NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
680
681         /* {"Options/---",              NULL, "---" }, */
682
683         {"Options/Priority",            NULL, N_("_Priority") },
684
685         {"Options/Encoding",            NULL, N_("Character _encoding") },
686         {"Options/Encoding/---",        NULL, "---" },
687 #define ENC_ACTION(cs_char,c_char,string) \
688         { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
689
690         {"Options/Encoding/Western",    NULL, N_("Western European") },
691         {"Options/Encoding/Baltic",     NULL, N_("Baltic") },
692         {"Options/Encoding/Hebrew",     NULL, N_("Hebrew") },
693         {"Options/Encoding/Arabic",     NULL, N_("Arabic") },
694         {"Options/Encoding/Cyrillic",   NULL, N_("Cyrillic") },
695         {"Options/Encoding/Japanese",   NULL, N_("Japanese") },
696         {"Options/Encoding/Chinese",    NULL, N_("Chinese") },
697         {"Options/Encoding/Korean",     NULL, N_("Korean") },
698         {"Options/Encoding/Thai",       NULL, N_("Thai") },
699
700 /* Tools menu */
701         {"Tools/AddressBook",           NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) }, 
702
703         {"Tools/Template",      NULL, N_("_Template") },
704         {"Tools/Template/PlaceHolder",  NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
705         {"Tools/Actions",       NULL, N_("Actio_ns") },
706         {"Tools/Actions/PlaceHolder",   NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
707
708 /* Help menu */
709         {"Help/About",          NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) }, 
710 };
711
712 static GtkToggleActionEntry compose_toggle_entries[] =
713 {
714         {"Edit/AutoWrap",               NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
715         {"Edit/AutoIndent",             NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
716         {"Options/Sign",                NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
717         {"Options/Encrypt",             NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
718         {"Options/RequestRetRcpt",      NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
719         {"Options/RemoveReferences",    NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
720         {"Tools/ShowRuler",             NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
721 };
722
723 static GtkRadioActionEntry compose_radio_rm_entries[] =
724 {
725         {"Options/ReplyMode/Normal",    NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
726         {"Options/ReplyMode/All",       NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
727         {"Options/ReplyMode/Sender",    NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
728         {"Options/ReplyMode/List",      NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
729 };
730
731 static GtkRadioActionEntry compose_radio_prio_entries[] =
732 {
733         {"Options/Priority/Highest",    NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
734         {"Options/Priority/High",       NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
735         {"Options/Priority/Normal",     NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
736         {"Options/Priority/Low",        NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
737         {"Options/Priority/Lowest",     NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
738 };
739
740 static GtkRadioActionEntry compose_radio_enc_entries[] =
741 {
742         ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
743         ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
744         ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
745         ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
746         ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
747         ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
748         ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
749         ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
750         ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
751         ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
752         ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
753         ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
754         ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
755         ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
756         ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
757         ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
758         ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
759         ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
760         ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
761         ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
762         ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
763         ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
764         ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
765         ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
766         ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
767         ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
768         ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
769         ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
770         ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
771         ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
772         ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
773         ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
774         ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
775 };
776
777 static GtkTargetEntry compose_mime_types[] =
778 {
779         {"text/uri-list", 0, 0},
780         {"UTF8_STRING", 0, 0},
781         {"text/plain", 0, 0}
782 };
783
784 static gboolean compose_put_existing_to_front(MsgInfo *info)
785 {
786         const GList *compose_list = compose_get_compose_list();
787         const GList *elem = NULL;
788         
789         if (compose_list) {
790                 for (elem = compose_list; elem != NULL && elem->data != NULL; 
791                      elem = elem->next) {
792                         Compose *c = (Compose*)elem->data;
793
794                         if (!c->targetinfo || !c->targetinfo->msgid ||
795                             !info->msgid)
796                                 continue;
797
798                         if (!strcmp(c->targetinfo->msgid, info->msgid)) {
799                                 gtkut_window_popup(c->window);
800                                 return TRUE;
801                         }
802                 }
803         }
804         return FALSE;
805 }
806
807 static GdkColor quote_color1 = 
808         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
809 static GdkColor quote_color2 = 
810         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
811 static GdkColor quote_color3 = 
812         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
813
814 static GdkColor quote_bgcolor1 = 
815         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
816 static GdkColor quote_bgcolor2 = 
817         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
818 static GdkColor quote_bgcolor3 = 
819         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
820
821 static GdkColor signature_color = {
822         (gulong)0,
823         (gushort)0x7fff,
824         (gushort)0x7fff,
825         (gushort)0x7fff
826 };
827
828 static GdkColor uri_color = {
829         (gulong)0,
830         (gushort)0,
831         (gushort)0,
832         (gushort)0
833 };
834
835 static void compose_create_tags(GtkTextView *text, Compose *compose)
836 {
837         GtkTextBuffer *buffer;
838         GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
839 #if !GTK_CHECK_VERSION(2, 24, 0)
840         GdkColormap *cmap;
841         gboolean success[8];
842         int i;
843         GdkColor color[8];
844 #endif
845
846         buffer = gtk_text_view_get_buffer(text);
847
848         if (prefs_common.enable_color) {
849                 /* grab the quote colors, converting from an int to a GdkColor */
850                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
851                                                &quote_color1);
852                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
853                                                &quote_color2);
854                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
855                                                &quote_color3);
856                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
857                                                &quote_bgcolor1);
858                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
859                                                &quote_bgcolor2);
860                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
861                                                &quote_bgcolor3);
862                 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
863                                                &signature_color);
864                 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
865                                                &uri_color);
866         } else {
867                 signature_color = quote_color1 = quote_color2 = quote_color3 = 
868                         quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
869         }
870
871         if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
872                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
873                                            "foreground-gdk", &quote_color1,
874                                            "paragraph-background-gdk", &quote_bgcolor1,
875                                            NULL);
876                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
877                                            "foreground-gdk", &quote_color2,
878                                            "paragraph-background-gdk", &quote_bgcolor2,
879                                            NULL);
880                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
881                                            "foreground-gdk", &quote_color3,
882                                            "paragraph-background-gdk", &quote_bgcolor3,
883                                            NULL);
884         } else {
885                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
886                                            "foreground-gdk", &quote_color1,
887                                            NULL);
888                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
889                                            "foreground-gdk", &quote_color2,
890                                            NULL);
891                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
892                                            "foreground-gdk", &quote_color3,
893                                            NULL);
894         }
895         
896         compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
897                                    "foreground-gdk", &signature_color,
898                                    NULL);
899         
900         compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
901                                         "foreground-gdk", &uri_color,
902                                          NULL);
903         compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
904         compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
905
906 #if !GTK_CHECK_VERSION(2, 24, 0)
907         color[0] = quote_color1;
908         color[1] = quote_color2;
909         color[2] = quote_color3;
910         color[3] = quote_bgcolor1;
911         color[4] = quote_bgcolor2;
912         color[5] = quote_bgcolor3;
913         color[6] = signature_color;
914         color[7] = uri_color;
915
916         cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
917         gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
918
919         for (i = 0; i < 8; i++) {
920                 if (success[i] == FALSE) {
921                         g_warning("Compose: color allocation failed.");
922                         quote_color1 = quote_color2 = quote_color3 = 
923                                 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = 
924                                 signature_color = uri_color = black;
925                 }
926         }
927 #endif
928 }
929
930 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
931                      GList *attach_files)
932 {
933         return compose_generic_new(account, mailto, NULL, attach_files, NULL);
934 }
935
936 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
937 {
938         return compose_generic_new(account, mailto, item, NULL, NULL);
939 }
940
941 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
942 {
943         return compose_generic_new( account, NULL, NULL, NULL, listAddress );
944 }
945
946 #define SCROLL_TO_CURSOR(compose) {                             \
947         GtkTextMark *cmark = gtk_text_buffer_get_insert(        \
948                 gtk_text_view_get_buffer(                       \
949                         GTK_TEXT_VIEW(compose->text)));         \
950         gtk_text_view_scroll_mark_onscreen(                     \
951                 GTK_TEXT_VIEW(compose->text),                   \
952                 cmark);                                         \
953 }
954
955 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
956 {
957         GtkEditable *entry;
958         if (folderidentifier) {
959 #if !GTK_CHECK_VERSION(2, 24, 0)
960                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
961 #else
962                 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
963 #endif
964                 prefs_common.compose_save_to_history = add_history(
965                                 prefs_common.compose_save_to_history, folderidentifier);
966 #if !GTK_CHECK_VERSION(2, 24, 0)
967                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
968                                 prefs_common.compose_save_to_history);
969 #else
970                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
971                                 prefs_common.compose_save_to_history);
972 #endif
973         }
974
975         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
976         if (folderidentifier)
977                 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
978         else
979                 gtk_entry_set_text(GTK_ENTRY(entry), "");
980 }
981
982 static gchar *compose_get_save_to(Compose *compose)
983 {
984         GtkEditable *entry;
985         gchar *result = NULL;
986         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
987         result = gtk_editable_get_chars(entry, 0, -1);
988         
989         if (result) {
990 #if !GTK_CHECK_VERSION(2, 24, 0)
991                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
992 #else
993                 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
994 #endif
995                 prefs_common.compose_save_to_history = add_history(
996                                 prefs_common.compose_save_to_history, result);
997 #if !GTK_CHECK_VERSION(2, 24, 0)
998                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
999                                 prefs_common.compose_save_to_history);
1000 #else
1001                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
1002                                 prefs_common.compose_save_to_history);
1003 #endif
1004         }
1005         return result;
1006 }
1007
1008 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
1009                              GList *attach_files, GList *listAddress )
1010 {
1011         Compose *compose;
1012         GtkTextView *textview;
1013         GtkTextBuffer *textbuf;
1014         GtkTextIter iter;
1015         const gchar *subject_format = NULL;
1016         const gchar *body_format = NULL;
1017         gchar *mailto_from = NULL;
1018         PrefsAccount *mailto_account = NULL;
1019         MsgInfo* dummyinfo = NULL;
1020         gint cursor_pos = -1;
1021         MailField mfield = NO_FIELD_PRESENT;
1022         gchar* buf;
1023         GtkTextMark *mark;
1024
1025         /* check if mailto defines a from */
1026         if (mailto && *mailto != '\0') {
1027                 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1028                 /* mailto defines a from, check if we can get account prefs from it,
1029                    if not, the account prefs will be guessed using other ways, but we'll keep
1030                    the from anyway */
1031                 if (mailto_from) {
1032                         mailto_account = account_find_from_address(mailto_from, TRUE);
1033                         if (mailto_account == NULL) {
1034                                 gchar *tmp_from;
1035                                 Xstrdup_a(tmp_from, mailto_from, return NULL);
1036                                 extract_address(tmp_from);
1037                                 mailto_account = account_find_from_address(tmp_from, TRUE);
1038                         }
1039                 }
1040                 if (mailto_account)
1041                         account = mailto_account;
1042         }
1043
1044         /* if no account prefs set from mailto, set if from folder prefs (if any) */
1045         if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1046                 account = account_find_from_id(item->prefs->default_account);
1047
1048         /* if no account prefs set, fallback to the current one */
1049         if (!account) account = cur_account;
1050         cm_return_val_if_fail(account != NULL, NULL);
1051
1052         compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1053
1054         /* override from name if mailto asked for it */
1055         if (mailto_from) {
1056                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1057                 g_free(mailto_from);
1058         } else
1059                 /* override from name according to folder properties */
1060                 if (item && item->prefs &&
1061                         item->prefs->compose_with_format &&
1062                         item->prefs->compose_override_from_format &&
1063                         *item->prefs->compose_override_from_format != '\0') {
1064
1065                         gchar *tmp = NULL;
1066                         gchar *buf = NULL;
1067
1068                         dummyinfo = compose_msginfo_new_from_compose(compose);
1069
1070                         /* decode \-escape sequences in the internal representation of the quote format */
1071                         tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1072                         pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1073
1074 #ifdef USE_ENCHANT
1075                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1076                                         compose->gtkaspell);
1077 #else
1078                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1079 #endif
1080                         quote_fmt_scan_string(tmp);
1081                         quote_fmt_parse();
1082
1083                         buf = quote_fmt_get_buffer();
1084                         if (buf == NULL)
1085                                 alertpanel_error(_("New message From format error."));
1086                         else
1087                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1088                         quote_fmt_reset_vartable();
1089
1090                         g_free(tmp);
1091                 }
1092
1093         compose->replyinfo = NULL;
1094         compose->fwdinfo   = NULL;
1095
1096         textview = GTK_TEXT_VIEW(compose->text);
1097         textbuf = gtk_text_view_get_buffer(textview);
1098         compose_create_tags(textview, compose);
1099
1100         undo_block(compose->undostruct);
1101 #ifdef USE_ENCHANT
1102         compose_set_dictionaries_from_folder_prefs(compose, item);
1103 #endif
1104
1105         if (account->auto_sig)
1106                 compose_insert_sig(compose, FALSE);
1107         gtk_text_buffer_get_start_iter(textbuf, &iter);
1108         gtk_text_buffer_place_cursor(textbuf, &iter);
1109
1110         if (account->protocol != A_NNTP) {
1111                 if (mailto && *mailto != '\0') {
1112                         mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1113
1114                 } else {
1115                         compose_set_folder_prefs(compose, item, TRUE);
1116                 }
1117                 if (item && item->ret_rcpt) {
1118                         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1119                 }
1120         } else {
1121                 if (mailto && *mailto != '\0') {
1122                         if (!strchr(mailto, '@'))
1123                                 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1124                         else
1125                                 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1126                 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1127                         compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1128                         mfield = TO_FIELD_PRESENT;
1129                 }
1130                 /*
1131                  * CLAWS: just don't allow return receipt request, even if the user
1132                  * may want to send an email. simple but foolproof.
1133                  */
1134                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE); 
1135         }
1136         compose_add_field_list( compose, listAddress );
1137
1138         if (item && item->prefs && item->prefs->compose_with_format) {
1139                 subject_format = item->prefs->compose_subject_format;
1140                 body_format = item->prefs->compose_body_format;
1141         } else if (account->compose_with_format) {
1142                 subject_format = account->compose_subject_format;
1143                 body_format = account->compose_body_format;
1144         } else if (prefs_common.compose_with_format) {
1145                 subject_format = prefs_common.compose_subject_format;
1146                 body_format = prefs_common.compose_body_format;
1147         }
1148
1149         if (subject_format || body_format) {
1150
1151                 if ( subject_format
1152                          && *subject_format != '\0' )
1153                 {
1154                         gchar *subject = NULL;
1155                         gchar *tmp = NULL;
1156                         gchar *buf = NULL;
1157
1158                         if (!dummyinfo)
1159                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1160
1161                         /* decode \-escape sequences in the internal representation of the quote format */
1162                         tmp = g_malloc(strlen(subject_format)+1);
1163                         pref_get_unescaped_pref(tmp, subject_format);
1164
1165                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1166 #ifdef USE_ENCHANT
1167                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1168                                         compose->gtkaspell);
1169 #else
1170                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1171 #endif
1172                         quote_fmt_scan_string(tmp);
1173                         quote_fmt_parse();
1174
1175                         buf = quote_fmt_get_buffer();
1176                         if (buf == NULL)
1177                                 alertpanel_error(_("New message subject format error."));
1178                         else
1179                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1180                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1181                         quote_fmt_reset_vartable();
1182
1183                         g_free(subject);
1184                         g_free(tmp);
1185                         mfield = SUBJECT_FIELD_PRESENT;
1186                 }
1187
1188                 if ( body_format
1189                          && *body_format != '\0' )
1190                 {
1191                         GtkTextView *text;
1192                         GtkTextBuffer *buffer;
1193                         GtkTextIter start, end;
1194                         gchar *tmp = NULL;
1195
1196                         if (!dummyinfo)
1197                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1198
1199                         text = GTK_TEXT_VIEW(compose->text);
1200                         buffer = gtk_text_view_get_buffer(text);
1201                         gtk_text_buffer_get_start_iter(buffer, &start);
1202                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1203                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1204
1205                         compose_quote_fmt(compose, dummyinfo,
1206                                           body_format,
1207                                           NULL, tmp, FALSE, TRUE,
1208                                                   _("The body of the \"New message\" template has an error at line %d."));
1209                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1210                         quote_fmt_reset_vartable();
1211
1212                         g_free(tmp);
1213 #ifdef USE_ENCHANT
1214                         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1215                                 gtkaspell_highlight_all(compose->gtkaspell);
1216 #endif
1217                         mfield = BODY_FIELD_PRESENT;
1218                 }
1219
1220         }
1221         procmsg_msginfo_free( &dummyinfo );
1222
1223         if (attach_files) {
1224                 GList *curr;
1225                 AttachInfo *ainfo;
1226
1227                 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1228                         ainfo = (AttachInfo *) curr->data;
1229                         compose_attach_append(compose, ainfo->file, ainfo->file,
1230                                         ainfo->content_type, ainfo->charset);
1231                 }
1232         }
1233
1234         compose_show_first_last_header(compose, TRUE);
1235
1236         /* Set save folder */
1237         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1238                 gchar *folderidentifier;
1239
1240                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1241                 folderidentifier = folder_item_get_identifier(item);
1242                 compose_set_save_to(compose, folderidentifier);
1243                 g_free(folderidentifier);
1244         }
1245
1246         /* Place cursor according to provided input (mfield) */
1247         switch (mfield) { 
1248                 case NO_FIELD_PRESENT:
1249                         if (compose->header_last)
1250                                 gtk_widget_grab_focus(compose->header_last->entry);
1251                         break;
1252                 case TO_FIELD_PRESENT:
1253                         buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1254                         if (buf) {
1255                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1256                                 g_free(buf);
1257                         }
1258                         gtk_widget_grab_focus(compose->subject_entry);
1259                         break;
1260                 case SUBJECT_FIELD_PRESENT:
1261                         textview = GTK_TEXT_VIEW(compose->text);
1262                         if (!textview)
1263                                 break;
1264                         textbuf = gtk_text_view_get_buffer(textview);
1265                         if (!textbuf)
1266                                 break;
1267                         mark = gtk_text_buffer_get_insert(textbuf);
1268                         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1269                         gtk_text_buffer_insert(textbuf, &iter, "", -1);
1270                     /* 
1271                      * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1272                      * only defers where it comes to the variable body
1273                      * is not null. If no body is present compose->text
1274                      * will be null in which case you cannot place the
1275                      * cursor inside the component so. An empty component
1276                      * is therefore created before placing the cursor
1277                      */
1278                 case BODY_FIELD_PRESENT:
1279                         cursor_pos = quote_fmt_get_cursor_pos();
1280                         if (cursor_pos == -1)
1281                                 gtk_widget_grab_focus(compose->header_last->entry);
1282                         else
1283                                 gtk_widget_grab_focus(compose->text);
1284                         break;
1285         }
1286
1287         undo_unblock(compose->undostruct);
1288
1289         if (prefs_common.auto_exteditor)
1290                 compose_exec_ext_editor(compose);
1291
1292         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1293
1294         SCROLL_TO_CURSOR(compose);
1295
1296         compose->modified = FALSE;
1297         compose_set_title(compose);
1298
1299         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1300
1301         return compose;
1302 }
1303
1304 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1305                 gboolean override_pref, const gchar *system)
1306 {
1307         const gchar *privacy = NULL;
1308
1309         cm_return_if_fail(compose != NULL);
1310         cm_return_if_fail(account != NULL);
1311
1312         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1313                 return;
1314
1315         if (account->default_privacy_system && strlen(account->default_privacy_system))
1316                 privacy = account->default_privacy_system;
1317         else if (system)
1318                 privacy = system;
1319         else {
1320                 GSList *privacy_avail = privacy_get_system_ids();
1321                 if (privacy_avail && g_slist_length(privacy_avail)) {
1322                         privacy = (gchar *)(privacy_avail->data);
1323                 }
1324         }
1325         if (privacy != NULL) {
1326                 if (system) {
1327                         g_free(compose->privacy_system);
1328                         compose->privacy_system = NULL;
1329                         g_free(compose->encdata);
1330                         compose->encdata = NULL;
1331                 }
1332                 if (compose->privacy_system == NULL)
1333                         compose->privacy_system = g_strdup(privacy);
1334                 else if (*(compose->privacy_system) == '\0') {
1335                         g_free(compose->privacy_system);
1336                         g_free(compose->encdata);
1337                         compose->encdata = NULL;
1338                         compose->privacy_system = g_strdup(privacy);
1339                 }
1340                 compose_update_privacy_system_menu_item(compose, FALSE);
1341                 compose_use_encryption(compose, TRUE);
1342         }
1343 }       
1344
1345 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1346 {
1347         const gchar *privacy = NULL;
1348
1349         if (account->default_privacy_system && strlen(account->default_privacy_system))
1350                 privacy = account->default_privacy_system;
1351         else if (system)
1352                 privacy = system;
1353         else {
1354                 GSList *privacy_avail = privacy_get_system_ids();
1355                 if (privacy_avail && g_slist_length(privacy_avail)) {
1356                         privacy = (gchar *)(privacy_avail->data);
1357                 }
1358         }
1359
1360         if (privacy != NULL) {
1361                 if (system) {
1362                         g_free(compose->privacy_system);
1363                         compose->privacy_system = NULL;
1364                         g_free(compose->encdata);
1365                         compose->encdata = NULL;
1366                 }
1367                 if (compose->privacy_system == NULL)
1368                         compose->privacy_system = g_strdup(privacy);
1369                 compose_update_privacy_system_menu_item(compose, FALSE);
1370                 compose_use_signing(compose, TRUE);
1371         }
1372 }       
1373
1374 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1375 {
1376         MsgInfo *msginfo;
1377         guint list_len;
1378         Compose *compose = NULL;
1379         
1380         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1381
1382         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1383         cm_return_val_if_fail(msginfo != NULL, NULL);
1384
1385         list_len = g_slist_length(msginfo_list);
1386
1387         switch (mode) {
1388         case COMPOSE_REPLY:
1389         case COMPOSE_REPLY_TO_ADDRESS:
1390                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1391                               FALSE, prefs_common.default_reply_list, FALSE, body);
1392                 break;
1393         case COMPOSE_REPLY_WITH_QUOTE:
1394                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1395                         FALSE, prefs_common.default_reply_list, FALSE, body);
1396                 break;
1397         case COMPOSE_REPLY_WITHOUT_QUOTE:
1398                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1399                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1400                 break;
1401         case COMPOSE_REPLY_TO_SENDER:
1402                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1403                               FALSE, FALSE, TRUE, body);
1404                 break;
1405         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1406                 compose = compose_followup_and_reply_to(msginfo,
1407                                               COMPOSE_QUOTE_CHECK,
1408                                               FALSE, FALSE, body);
1409                 break;
1410         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1411                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1412                         FALSE, FALSE, TRUE, body);
1413                 break;
1414         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1415                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1416                         FALSE, FALSE, TRUE, NULL);
1417                 break;
1418         case COMPOSE_REPLY_TO_ALL:
1419                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1420                         TRUE, FALSE, FALSE, body);
1421                 break;
1422         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1423                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1424                         TRUE, FALSE, FALSE, body);
1425                 break;
1426         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1427                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1428                         TRUE, FALSE, FALSE, NULL);
1429                 break;
1430         case COMPOSE_REPLY_TO_LIST:
1431                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1432                         FALSE, TRUE, FALSE, body);
1433                 break;
1434         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1435                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1436                         FALSE, TRUE, FALSE, body);
1437                 break;
1438         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1439                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1440                         FALSE, TRUE, FALSE, NULL);
1441                 break;
1442         case COMPOSE_FORWARD:
1443                 if (prefs_common.forward_as_attachment) {
1444                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1445                         return compose;
1446                 } else {
1447                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1448                         return compose;
1449                 }
1450                 break;
1451         case COMPOSE_FORWARD_INLINE:
1452                 /* check if we reply to more than one Message */
1453                 if (list_len == 1) {
1454                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1455                         break;
1456                 } 
1457                 /* more messages FALL THROUGH */
1458         case COMPOSE_FORWARD_AS_ATTACH:
1459                 compose = compose_forward_multiple(NULL, msginfo_list);
1460                 break;
1461         case COMPOSE_REDIRECT:
1462                 compose = compose_redirect(NULL, msginfo, FALSE);
1463                 break;
1464         default:
1465                 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1466         }
1467         
1468         if (compose == NULL) {
1469                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1470                 return NULL;
1471         }
1472
1473         compose->rmode = mode;
1474         switch (compose->rmode) {
1475         case COMPOSE_REPLY:
1476         case COMPOSE_REPLY_WITH_QUOTE:
1477         case COMPOSE_REPLY_WITHOUT_QUOTE:
1478         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1479                 debug_print("reply mode Normal\n");
1480                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1481                 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1482                 break;
1483         case COMPOSE_REPLY_TO_SENDER:
1484         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1485         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1486                 debug_print("reply mode Sender\n");
1487                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1488                 break;
1489         case COMPOSE_REPLY_TO_ALL:
1490         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1491         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1492                 debug_print("reply mode All\n");
1493                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1494                 break;
1495         case COMPOSE_REPLY_TO_LIST:
1496         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1497         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1498                 debug_print("reply mode List\n");
1499                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1500                 break;
1501         case COMPOSE_REPLY_TO_ADDRESS:
1502                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1503                 break;
1504         default:
1505                 break;
1506         }
1507         return compose;
1508 }
1509
1510 static Compose *compose_reply(MsgInfo *msginfo,
1511                                    ComposeQuoteMode quote_mode,
1512                                    gboolean to_all,
1513                                    gboolean to_ml,
1514                                    gboolean to_sender, 
1515                                    const gchar *body)
1516 {
1517         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1518                               to_sender, FALSE, body);
1519 }
1520
1521 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1522                                    ComposeQuoteMode quote_mode,
1523                                    gboolean to_all,
1524                                    gboolean to_sender,
1525                                    const gchar *body)
1526 {
1527         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1528                               to_sender, TRUE, body);
1529 }
1530
1531 static void compose_extract_original_charset(Compose *compose)
1532 {
1533         MsgInfo *info = NULL;
1534         if (compose->replyinfo) {
1535                 info = compose->replyinfo;
1536         } else if (compose->fwdinfo) {
1537                 info = compose->fwdinfo;
1538         } else if (compose->targetinfo) {
1539                 info = compose->targetinfo;
1540         }
1541         if (info) {
1542                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1543                 MimeInfo *partinfo = mimeinfo;
1544                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1545                         partinfo = procmime_mimeinfo_next(partinfo);
1546                 if (partinfo) {
1547                         compose->orig_charset = 
1548                                 g_strdup(procmime_mimeinfo_get_parameter(
1549                                                 partinfo, "charset"));
1550                 }
1551                 procmime_mimeinfo_free_all(&mimeinfo);
1552         }
1553 }
1554
1555 #define SIGNAL_BLOCK(buffer) {                                  \
1556         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1557                                 G_CALLBACK(compose_changed_cb), \
1558                                 compose);                       \
1559         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1560                                 G_CALLBACK(text_inserted),      \
1561                                 compose);                       \
1562 }
1563
1564 #define SIGNAL_UNBLOCK(buffer) {                                \
1565         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1566                                 G_CALLBACK(compose_changed_cb), \
1567                                 compose);                       \
1568         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1569                                 G_CALLBACK(text_inserted),      \
1570                                 compose);                       \
1571 }
1572
1573 static Compose *compose_generic_reply(MsgInfo *msginfo,
1574                                   ComposeQuoteMode quote_mode,
1575                                   gboolean to_all, gboolean to_ml,
1576                                   gboolean to_sender,
1577                                   gboolean followup_and_reply_to,
1578                                   const gchar *body)
1579 {
1580         Compose *compose;
1581         PrefsAccount *account = NULL;
1582         GtkTextView *textview;
1583         GtkTextBuffer *textbuf;
1584         gboolean quote = FALSE;
1585         const gchar *qmark = NULL;
1586         const gchar *body_fmt = NULL;
1587         gchar *s_system = NULL;
1588         START_TIMING("");
1589         cm_return_val_if_fail(msginfo != NULL, NULL);
1590         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1591
1592         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1593
1594         cm_return_val_if_fail(account != NULL, NULL);
1595
1596         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1597
1598         compose->updating = TRUE;
1599
1600         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1601         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1602
1603         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1604         if (!compose->replyinfo)
1605                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1606
1607         compose_extract_original_charset(compose);
1608         
1609         if (msginfo->folder && msginfo->folder->ret_rcpt)
1610                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1611
1612         /* Set save folder */
1613         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1614                 gchar *folderidentifier;
1615
1616                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1617                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1618                 compose_set_save_to(compose, folderidentifier);
1619                 g_free(folderidentifier);
1620         }
1621
1622         if (compose_parse_header(compose, msginfo) < 0) {
1623                 compose->updating = FALSE;
1624                 compose_destroy(compose);
1625                 return NULL;
1626         }
1627
1628         /* override from name according to folder properties */
1629         if (msginfo->folder && msginfo->folder->prefs &&
1630                 msginfo->folder->prefs->reply_with_format &&
1631                 msginfo->folder->prefs->reply_override_from_format &&
1632                 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1633
1634                 gchar *tmp = NULL;
1635                 gchar *buf = NULL;
1636
1637                 /* decode \-escape sequences in the internal representation of the quote format */
1638                 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1639                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1640
1641 #ifdef USE_ENCHANT
1642                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1643                                 compose->gtkaspell);
1644 #else
1645                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1646 #endif
1647                 quote_fmt_scan_string(tmp);
1648                 quote_fmt_parse();
1649
1650                 buf = quote_fmt_get_buffer();
1651                 if (buf == NULL)
1652                         alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1653                 else
1654                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1655                 quote_fmt_reset_vartable();
1656
1657                 g_free(tmp);
1658         }
1659
1660         textview = (GTK_TEXT_VIEW(compose->text));
1661         textbuf = gtk_text_view_get_buffer(textview);
1662         compose_create_tags(textview, compose);
1663
1664         undo_block(compose->undostruct);
1665 #ifdef USE_ENCHANT
1666         compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1667         gtkaspell_block_check(compose->gtkaspell);
1668 #endif
1669
1670         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1671                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1672                 /* use the reply format of folder (if enabled), or the account's one
1673                    (if enabled) or fallback to the global reply format, which is always
1674                    enabled (even if empty), and use the relevant quotemark */
1675                 quote = TRUE;
1676                 if (msginfo->folder && msginfo->folder->prefs &&
1677                                 msginfo->folder->prefs->reply_with_format) {
1678                         qmark = msginfo->folder->prefs->reply_quotemark;
1679                         body_fmt = msginfo->folder->prefs->reply_body_format;
1680
1681                 } else if (account->reply_with_format) {
1682                         qmark = account->reply_quotemark;
1683                         body_fmt = account->reply_body_format;
1684
1685                 } else {
1686                         qmark = prefs_common.quotemark;
1687                         if (prefs_common.quotefmt && *prefs_common.quotefmt)
1688                                 body_fmt = gettext(prefs_common.quotefmt);
1689                         else
1690                                 body_fmt = "";
1691                 }
1692         }
1693
1694         if (quote) {
1695                 /* empty quotemark is not allowed */
1696                 if (qmark == NULL || *qmark == '\0')
1697                         qmark = "> ";
1698                 compose_quote_fmt(compose, compose->replyinfo,
1699                                   body_fmt, qmark, body, FALSE, TRUE,
1700                                           _("The body of the \"Reply\" template has an error at line %d."));
1701                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1702                 quote_fmt_reset_vartable();
1703         }
1704
1705         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1706                 compose_force_encryption(compose, account, FALSE, s_system);
1707         }
1708
1709         privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1710         if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1711                 compose_force_signing(compose, account, s_system);
1712         }
1713         g_free(s_system);
1714
1715         SIGNAL_BLOCK(textbuf);
1716         
1717         if (account->auto_sig)
1718                 compose_insert_sig(compose, FALSE);
1719
1720         compose_wrap_all(compose);
1721
1722 #ifdef USE_ENCHANT
1723         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1724                 gtkaspell_highlight_all(compose->gtkaspell);
1725         gtkaspell_unblock_check(compose->gtkaspell);
1726 #endif
1727         SIGNAL_UNBLOCK(textbuf);
1728         
1729         gtk_widget_grab_focus(compose->text);
1730
1731         undo_unblock(compose->undostruct);
1732
1733         if (prefs_common.auto_exteditor)
1734                 compose_exec_ext_editor(compose);
1735                 
1736         compose->modified = FALSE;
1737         compose_set_title(compose);
1738
1739         compose->updating = FALSE;
1740         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1741         SCROLL_TO_CURSOR(compose);
1742         
1743         if (compose->deferred_destroy) {
1744                 compose_destroy(compose);
1745                 return NULL;
1746         }
1747         END_TIMING();
1748
1749         return compose;
1750 }
1751
1752 #define INSERT_FW_HEADER(var, hdr) \
1753 if (msginfo->var && *msginfo->var) { \
1754         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1755         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1756         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1757 }
1758
1759 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1760                          gboolean as_attach, const gchar *body,
1761                          gboolean no_extedit,
1762                          gboolean batch)
1763 {
1764         Compose *compose;
1765         GtkTextView *textview;
1766         GtkTextBuffer *textbuf;
1767         gint cursor_pos = -1;
1768         ComposeMode mode;
1769
1770         cm_return_val_if_fail(msginfo != NULL, NULL);
1771         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1772
1773         if (!account && 
1774             !(account = compose_guess_forward_account_from_msginfo
1775                                 (msginfo)))
1776                 account = cur_account;
1777
1778         if (!prefs_common.forward_as_attachment)
1779                 mode = COMPOSE_FORWARD_INLINE;
1780         else
1781                 mode = COMPOSE_FORWARD;
1782         compose = compose_create(account, msginfo->folder, mode, batch);
1783
1784         compose->updating = TRUE;
1785         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1786         if (!compose->fwdinfo)
1787                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1788
1789         compose_extract_original_charset(compose);
1790
1791         if (msginfo->subject && *msginfo->subject) {
1792                 gchar *buf, *buf2, *p;
1793
1794                 buf = p = g_strdup(msginfo->subject);
1795                 p += subject_get_prefix_length(p);
1796                 memmove(buf, p, strlen(p) + 1);
1797
1798                 buf2 = g_strdup_printf("Fw: %s", buf);
1799                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1800                 
1801                 g_free(buf);
1802                 g_free(buf2);
1803         }
1804
1805         /* override from name according to folder properties */
1806         if (msginfo->folder && msginfo->folder->prefs &&
1807                 msginfo->folder->prefs->forward_with_format &&
1808                 msginfo->folder->prefs->forward_override_from_format &&
1809                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1810
1811                 gchar *tmp = NULL;
1812                 gchar *buf = NULL;
1813                 MsgInfo *full_msginfo = NULL;
1814
1815                 if (!as_attach)
1816                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1817                 if (!full_msginfo)
1818                         full_msginfo = procmsg_msginfo_copy(msginfo);
1819
1820                 /* decode \-escape sequences in the internal representation of the quote format */
1821                 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1822                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1823
1824 #ifdef USE_ENCHANT
1825                 gtkaspell_block_check(compose->gtkaspell);
1826                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1827                                 compose->gtkaspell);
1828 #else
1829                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1830 #endif
1831                 quote_fmt_scan_string(tmp);
1832                 quote_fmt_parse();
1833
1834                 buf = quote_fmt_get_buffer();
1835                 if (buf == NULL)
1836                         alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1837                 else
1838                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1839                 quote_fmt_reset_vartable();
1840
1841                 g_free(tmp);
1842                 procmsg_msginfo_free(&full_msginfo);
1843         }
1844
1845         textview = GTK_TEXT_VIEW(compose->text);
1846         textbuf = gtk_text_view_get_buffer(textview);
1847         compose_create_tags(textview, compose);
1848         
1849         undo_block(compose->undostruct);
1850         if (as_attach) {
1851                 gchar *msgfile;
1852
1853                 msgfile = procmsg_get_message_file(msginfo);
1854                 if (!is_file_exist(msgfile))
1855                         g_warning("%s: file does not exist", msgfile);
1856                 else
1857                         compose_attach_append(compose, msgfile, msgfile,
1858                                               "message/rfc822", NULL);
1859
1860                 g_free(msgfile);
1861         } else {
1862                 const gchar *qmark = NULL;
1863                 const gchar *body_fmt = NULL;
1864                 MsgInfo *full_msginfo;
1865
1866                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1867                 if (!full_msginfo)
1868                         full_msginfo = procmsg_msginfo_copy(msginfo);
1869
1870                 /* use the forward format of folder (if enabled), or the account's one
1871                    (if enabled) or fallback to the global forward format, which is always
1872                    enabled (even if empty), and use the relevant quotemark */
1873                 if (msginfo->folder && msginfo->folder->prefs &&
1874                                 msginfo->folder->prefs->forward_with_format) {
1875                         qmark = msginfo->folder->prefs->forward_quotemark;
1876                         body_fmt = msginfo->folder->prefs->forward_body_format;
1877
1878                 } else if (account->forward_with_format) {
1879                         qmark = account->forward_quotemark;
1880                         body_fmt = account->forward_body_format;
1881
1882                 } else {
1883                         qmark = prefs_common.fw_quotemark;
1884                         if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1885                                 body_fmt = gettext(prefs_common.fw_quotefmt);
1886                         else
1887                                 body_fmt = "";
1888                 }
1889
1890                 /* empty quotemark is not allowed */
1891                 if (qmark == NULL || *qmark == '\0')
1892                         qmark = "> ";
1893
1894                 compose_quote_fmt(compose, full_msginfo,
1895                                   body_fmt, qmark, body, FALSE, TRUE,
1896                                           _("The body of the \"Forward\" template has an error at line %d."));
1897                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1898                 quote_fmt_reset_vartable();
1899                 compose_attach_parts(compose, msginfo);
1900
1901                 procmsg_msginfo_free(&full_msginfo);
1902         }
1903
1904         SIGNAL_BLOCK(textbuf);
1905
1906         if (account->auto_sig)
1907                 compose_insert_sig(compose, FALSE);
1908
1909         compose_wrap_all(compose);
1910
1911 #ifdef USE_ENCHANT
1912         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1913                 gtkaspell_highlight_all(compose->gtkaspell);
1914         gtkaspell_unblock_check(compose->gtkaspell);
1915 #endif
1916         SIGNAL_UNBLOCK(textbuf);
1917         
1918         cursor_pos = quote_fmt_get_cursor_pos();
1919         if (cursor_pos == -1)
1920                 gtk_widget_grab_focus(compose->header_last->entry);
1921         else
1922                 gtk_widget_grab_focus(compose->text);
1923
1924         if (!no_extedit && prefs_common.auto_exteditor)
1925                 compose_exec_ext_editor(compose);
1926         
1927         /*save folder*/
1928         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1929                 gchar *folderidentifier;
1930
1931                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1932                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1933                 compose_set_save_to(compose, folderidentifier);
1934                 g_free(folderidentifier);
1935         }
1936
1937         undo_unblock(compose->undostruct);
1938         
1939         compose->modified = FALSE;
1940         compose_set_title(compose);
1941
1942         compose->updating = FALSE;
1943         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1944         SCROLL_TO_CURSOR(compose);
1945
1946         if (compose->deferred_destroy) {
1947                 compose_destroy(compose);
1948                 return NULL;
1949         }
1950
1951         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1952
1953         return compose;
1954 }
1955
1956 #undef INSERT_FW_HEADER
1957
1958 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1959 {
1960         Compose *compose;
1961         GtkTextView *textview;
1962         GtkTextBuffer *textbuf;
1963         GtkTextIter iter;
1964         GSList *msginfo;
1965         gchar *msgfile;
1966         gboolean single_mail = TRUE;
1967         
1968         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1969
1970         if (g_slist_length(msginfo_list) > 1)
1971                 single_mail = FALSE;
1972
1973         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1974                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1975                         return NULL;
1976
1977         /* guess account from first selected message */
1978         if (!account && 
1979             !(account = compose_guess_forward_account_from_msginfo
1980                                 (msginfo_list->data)))
1981                 account = cur_account;
1982
1983         cm_return_val_if_fail(account != NULL, NULL);
1984
1985         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1986                 if (msginfo->data) {
1987                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1988                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1989                 }
1990         }
1991
1992         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1993                 g_warning("no msginfo_list");
1994                 return NULL;
1995         }
1996
1997         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1998
1999         compose->updating = TRUE;
2000
2001         /* override from name according to folder properties */
2002         if (msginfo_list->data) {
2003                 MsgInfo *msginfo = msginfo_list->data;
2004
2005                 if (msginfo->folder && msginfo->folder->prefs &&
2006                         msginfo->folder->prefs->forward_with_format &&
2007                         msginfo->folder->prefs->forward_override_from_format &&
2008                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
2009
2010                         gchar *tmp = NULL;
2011                         gchar *buf = NULL;
2012
2013                         /* decode \-escape sequences in the internal representation of the quote format */
2014                         tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2015                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2016
2017 #ifdef USE_ENCHANT
2018                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2019                                         compose->gtkaspell);
2020 #else
2021                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2022 #endif
2023                         quote_fmt_scan_string(tmp);
2024                         quote_fmt_parse();
2025
2026                         buf = quote_fmt_get_buffer();
2027                         if (buf == NULL)
2028                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2029                         else
2030                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2031                         quote_fmt_reset_vartable();
2032
2033                         g_free(tmp);
2034                 }
2035         }
2036
2037         textview = GTK_TEXT_VIEW(compose->text);
2038         textbuf = gtk_text_view_get_buffer(textview);
2039         compose_create_tags(textview, compose);
2040         
2041         undo_block(compose->undostruct);
2042         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2043                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2044
2045                 if (!is_file_exist(msgfile))
2046                         g_warning("%s: file does not exist", msgfile);
2047                 else
2048                         compose_attach_append(compose, msgfile, msgfile,
2049                                 "message/rfc822", NULL);
2050                 g_free(msgfile);
2051         }
2052         
2053         if (single_mail) {
2054                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2055                 if (info->subject && *info->subject) {
2056                         gchar *buf, *buf2, *p;
2057
2058                         buf = p = g_strdup(info->subject);
2059                         p += subject_get_prefix_length(p);
2060                         memmove(buf, p, strlen(p) + 1);
2061
2062                         buf2 = g_strdup_printf("Fw: %s", buf);
2063                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2064
2065                         g_free(buf);
2066                         g_free(buf2);
2067                 }
2068         } else {
2069                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2070                         _("Fw: multiple emails"));
2071         }
2072
2073         SIGNAL_BLOCK(textbuf);
2074         
2075         if (account->auto_sig)
2076                 compose_insert_sig(compose, FALSE);
2077
2078         compose_wrap_all(compose);
2079
2080         SIGNAL_UNBLOCK(textbuf);
2081         
2082         gtk_text_buffer_get_start_iter(textbuf, &iter);
2083         gtk_text_buffer_place_cursor(textbuf, &iter);
2084
2085         if (prefs_common.auto_exteditor)
2086                 compose_exec_ext_editor(compose);
2087
2088         gtk_widget_grab_focus(compose->header_last->entry);
2089         undo_unblock(compose->undostruct);
2090         compose->modified = FALSE;
2091         compose_set_title(compose);
2092
2093         compose->updating = FALSE;
2094         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2095         SCROLL_TO_CURSOR(compose);
2096
2097         if (compose->deferred_destroy) {
2098                 compose_destroy(compose);
2099                 return NULL;
2100         }
2101
2102         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2103
2104         return compose;
2105 }
2106
2107 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2108 {
2109         GtkTextIter start = *iter;
2110         GtkTextIter end_iter;
2111         int start_pos = gtk_text_iter_get_offset(&start);
2112         gchar *str = NULL;
2113         if (!compose->account->sig_sep)
2114                 return FALSE;
2115         
2116         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2117                 start_pos+strlen(compose->account->sig_sep));
2118
2119         /* check sig separator */
2120         str = gtk_text_iter_get_text(&start, &end_iter);
2121         if (!strcmp(str, compose->account->sig_sep)) {
2122                 gchar *tmp = NULL;
2123                 /* check end of line (\n) */
2124                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2125                         start_pos+strlen(compose->account->sig_sep));
2126                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2127                         start_pos+strlen(compose->account->sig_sep)+1);
2128                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2129                 if (!strcmp(tmp,"\n")) {
2130                         g_free(str);
2131                         g_free(tmp);
2132                         return TRUE;
2133                 }
2134                 g_free(tmp);    
2135         }
2136         g_free(str);
2137
2138         return FALSE;
2139 }
2140
2141 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2142 {
2143         FolderUpdateData *hookdata = (FolderUpdateData *)source;
2144         Compose *compose = (Compose *)data;
2145         FolderItem *old_item = NULL;
2146         FolderItem *new_item = NULL;
2147         gchar *old_id, *new_id;
2148
2149         if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2150          && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2151                 return FALSE;
2152
2153         old_item = hookdata->item;
2154         new_item = hookdata->item2;
2155
2156         old_id = folder_item_get_identifier(old_item);
2157         new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2158
2159         if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2160                 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2161                 compose->targetinfo->folder = new_item;
2162         }
2163
2164         if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2165                 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2166                 compose->replyinfo->folder = new_item;
2167         }
2168
2169         if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2170                 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2171                 compose->fwdinfo->folder = new_item;
2172         }
2173
2174         g_free(old_id);
2175         g_free(new_id);
2176         return FALSE;
2177 }
2178
2179 static void compose_colorize_signature(Compose *compose)
2180 {
2181         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2182         GtkTextIter iter;
2183         GtkTextIter end_iter;
2184         gtk_text_buffer_get_start_iter(buffer, &iter);
2185         while (gtk_text_iter_forward_line(&iter))
2186                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2187                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2188                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2189                 }
2190 }
2191
2192 #define BLOCK_WRAP() {                                                  \
2193         prev_autowrap = compose->autowrap;                              \
2194         buffer = gtk_text_view_get_buffer(                              \
2195                                         GTK_TEXT_VIEW(compose->text));  \
2196         compose->autowrap = FALSE;                                      \
2197                                                                         \
2198         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2199                                 G_CALLBACK(compose_changed_cb),         \
2200                                 compose);                               \
2201         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2202                                 G_CALLBACK(text_inserted),              \
2203                                 compose);                               \
2204 }
2205 #define UNBLOCK_WRAP() {                                                        \
2206         compose->autowrap = prev_autowrap;                                      \
2207         if (compose->autowrap) {                                                \
2208                 gint old = compose->draft_timeout_tag;                          \
2209                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;   \
2210                 compose_wrap_all(compose);                                      \
2211                 compose->draft_timeout_tag = old;                               \
2212         }                                                                       \
2213                                                                                 \
2214         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
2215                                 G_CALLBACK(compose_changed_cb),                 \
2216                                 compose);                                       \
2217         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
2218                                 G_CALLBACK(text_inserted),                      \
2219                                 compose);                                       \
2220 }
2221
2222 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2223 {
2224         Compose *compose = NULL;
2225         PrefsAccount *account = NULL;
2226         GtkTextView *textview;
2227         GtkTextBuffer *textbuf;
2228         GtkTextMark *mark;
2229         GtkTextIter iter;
2230         FILE *fp;
2231         gchar buf[BUFFSIZE];
2232         gboolean use_signing = FALSE;
2233         gboolean use_encryption = FALSE;
2234         gchar *privacy_system = NULL;
2235         int priority = PRIORITY_NORMAL;
2236         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2237         gboolean autowrap = prefs_common.autowrap;
2238         gboolean autoindent = prefs_common.auto_indent;
2239         HeaderEntry *manual_headers = NULL;
2240
2241         cm_return_val_if_fail(msginfo != NULL, NULL);
2242         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2243
2244         if (compose_put_existing_to_front(msginfo)) {
2245                 return NULL;
2246         }
2247
2248         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2249             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2250             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2251                 gchar queueheader_buf[BUFFSIZE];
2252                 gint id, param;
2253
2254                 /* Select Account from queue headers */
2255                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2256                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2257                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2258                         account = account_find_from_id(id);
2259                 }
2260                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2261                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2262                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2263                         account = account_find_from_id(id);
2264                 }
2265                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2266                                              sizeof(queueheader_buf), "NAID:")) {
2267                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2268                         account = account_find_from_id(id);
2269                 }
2270                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2271                                                     sizeof(queueheader_buf), "MAID:")) {
2272                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2273                         account = account_find_from_id(id);
2274                 }
2275                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2276                                                                 sizeof(queueheader_buf), "S:")) {
2277                         account = account_find_from_address(queueheader_buf, FALSE);
2278                 }
2279                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2280                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
2281                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2282                         use_signing = param;
2283                         
2284                 }
2285                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2286                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2287                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2288                         use_signing = param;
2289                         
2290                 }
2291                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2292                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2293                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2294                         use_encryption = param;
2295                 }
2296                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2297                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2298                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2299                         use_encryption = param;
2300                 }
2301                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2302                                              sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2303                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2304                         autowrap = param;
2305                 }
2306                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2307                                              sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2308                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2309                         autoindent = param;
2310                 }
2311                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2312                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2313                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2314                 }
2315                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2316                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2317                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2318                 }
2319                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2320                                              sizeof(queueheader_buf), "X-Priority: ")) {
2321                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2322                         priority = param;
2323                 }
2324                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2325                                              sizeof(queueheader_buf), "RMID:")) {
2326                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2327                         if (tokens[0] && tokens[1] && tokens[2]) {
2328                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2329                                 if (orig_item != NULL) {
2330                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2331                                 }
2332                         }
2333                         g_strfreev(tokens);
2334                 }
2335                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2336                                              sizeof(queueheader_buf), "FMID:")) {
2337                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2338                         if (tokens[0] && tokens[1] && tokens[2]) {
2339                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2340                                 if (orig_item != NULL) {
2341                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2342                                 }
2343                         }
2344                         g_strfreev(tokens);
2345                 }
2346                 /* Get manual headers */
2347                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2348                         gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2349                         if (*listmh != '\0') {
2350                                 debug_print("Got manual headers: %s\n", listmh);
2351                                 manual_headers = procheader_entries_from_str(listmh);
2352                         }
2353                         g_free(listmh);
2354                 }
2355         } else {
2356                 account = msginfo->folder->folder->account;
2357         }
2358
2359         if (!account && prefs_common.reedit_account_autosel) {
2360                 gchar from[BUFFSIZE];
2361                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2362                         extract_address(from);
2363                         account = account_find_from_address(from, FALSE);
2364                 }
2365         }
2366         if (!account) {
2367                 account = cur_account;
2368         }
2369         cm_return_val_if_fail(account != NULL, NULL);
2370
2371         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2372
2373         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2374         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2375         compose->autowrap = autowrap;
2376         compose->replyinfo = replyinfo;
2377         compose->fwdinfo = fwdinfo;
2378
2379         compose->updating = TRUE;
2380         compose->priority = priority;
2381
2382         if (privacy_system != NULL) {
2383                 compose->privacy_system = privacy_system;
2384                 compose_use_signing(compose, use_signing);
2385                 compose_use_encryption(compose, use_encryption);
2386                 compose_update_privacy_system_menu_item(compose, FALSE);
2387         } else {
2388                 activate_privacy_system(compose, account, FALSE);
2389         }
2390
2391         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2392
2393         compose_extract_original_charset(compose);
2394
2395         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2396             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2397             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2398                 gchar queueheader_buf[BUFFSIZE];
2399
2400                 /* Set message save folder */
2401                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2402                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2403                         compose_set_save_to(compose, &queueheader_buf[4]);
2404                 }
2405                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2406                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2407                         if (active) {
2408                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2409                         }
2410                 }
2411         }
2412         
2413         if (compose_parse_header(compose, msginfo) < 0) {
2414                 compose->updating = FALSE;
2415                 compose_destroy(compose);
2416                 return NULL;
2417         }
2418         compose_reedit_set_entry(compose, msginfo);
2419
2420         textview = GTK_TEXT_VIEW(compose->text);
2421         textbuf = gtk_text_view_get_buffer(textview);
2422         compose_create_tags(textview, compose);
2423
2424         mark = gtk_text_buffer_get_insert(textbuf);
2425         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2426
2427         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2428                                         G_CALLBACK(compose_changed_cb),
2429                                         compose);
2430         
2431         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2432                 fp = procmime_get_first_encrypted_text_content(msginfo);
2433                 if (fp) {
2434                         compose_force_encryption(compose, account, TRUE, NULL);
2435                 }
2436         } else {
2437                 fp = procmime_get_first_text_content(msginfo);
2438         }
2439         if (fp == NULL) {
2440                 g_warning("Can't get text part");
2441         }
2442
2443         if (fp != NULL) {
2444                 gboolean prev_autowrap;
2445                 GtkTextBuffer *buffer;
2446                 BLOCK_WRAP();
2447                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2448                         strcrchomp(buf);
2449                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2450                 }
2451                 UNBLOCK_WRAP();
2452                 fclose(fp);
2453         }
2454         
2455         compose_attach_parts(compose, msginfo);
2456
2457         compose_colorize_signature(compose);
2458
2459         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2460                                         G_CALLBACK(compose_changed_cb),
2461                                         compose);
2462
2463         if (manual_headers != NULL) {
2464                 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2465                         procheader_entries_free(manual_headers);
2466                         compose->updating = FALSE;
2467                         compose_destroy(compose);
2468                         return NULL;
2469                 }
2470                 procheader_entries_free(manual_headers);
2471         }
2472
2473         gtk_widget_grab_focus(compose->text);
2474
2475         if (prefs_common.auto_exteditor) {
2476                 compose_exec_ext_editor(compose);
2477         }
2478         compose->modified = FALSE;
2479         compose_set_title(compose);
2480
2481         compose->updating = FALSE;
2482         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2483         SCROLL_TO_CURSOR(compose);
2484
2485         if (compose->deferred_destroy) {
2486                 compose_destroy(compose);
2487                 return NULL;
2488         }
2489         
2490         compose->sig_str = account_get_signature_str(compose->account);
2491         
2492         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2493
2494         return compose;
2495 }
2496
2497 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2498                                                  gboolean batch)
2499 {
2500         Compose *compose;
2501         gchar *filename;
2502         FolderItem *item;
2503
2504         cm_return_val_if_fail(msginfo != NULL, NULL);
2505
2506         if (!account)
2507                 account = account_get_reply_account(msginfo,
2508                                         prefs_common.reply_account_autosel);
2509         cm_return_val_if_fail(account != NULL, NULL);
2510
2511         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2512
2513         compose->updating = TRUE;
2514
2515         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2516         compose->replyinfo = NULL;
2517         compose->fwdinfo = NULL;
2518
2519         compose_show_first_last_header(compose, TRUE);
2520
2521         gtk_widget_grab_focus(compose->header_last->entry);
2522
2523         filename = procmsg_get_message_file(msginfo);
2524
2525         if (filename == NULL) {
2526                 compose->updating = FALSE;
2527                 compose_destroy(compose);
2528
2529                 return NULL;
2530         }
2531
2532         compose->redirect_filename = filename;
2533         
2534         /* Set save folder */
2535         item = msginfo->folder;
2536         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2537                 gchar *folderidentifier;
2538
2539                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2540                 folderidentifier = folder_item_get_identifier(item);
2541                 compose_set_save_to(compose, folderidentifier);
2542                 g_free(folderidentifier);
2543         }
2544
2545         compose_attach_parts(compose, msginfo);
2546
2547         if (msginfo->subject)
2548                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2549                                    msginfo->subject);
2550         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2551
2552         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2553                                           _("The body of the \"Redirect\" template has an error at line %d."));
2554         quote_fmt_reset_vartable();
2555         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2556
2557         compose_colorize_signature(compose);
2558
2559         
2560         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2561         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2562         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2563
2564         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2565         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2566         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2567         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2568         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2569         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2570         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2571         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2572         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2573         
2574         if (compose->toolbar->draft_btn)
2575                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2576         if (compose->toolbar->insert_btn)
2577                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2578         if (compose->toolbar->attach_btn)
2579                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2580         if (compose->toolbar->sig_btn)
2581                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2582         if (compose->toolbar->exteditor_btn)
2583                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2584         if (compose->toolbar->linewrap_current_btn)
2585                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2586         if (compose->toolbar->linewrap_all_btn)
2587                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2588
2589         compose->modified = FALSE;
2590         compose_set_title(compose);
2591         compose->updating = FALSE;
2592         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2593         SCROLL_TO_CURSOR(compose);
2594
2595         if (compose->deferred_destroy) {
2596                 compose_destroy(compose);
2597                 return NULL;
2598         }
2599         
2600         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2601
2602         return compose;
2603 }
2604
2605 const GList *compose_get_compose_list(void)
2606 {
2607         return compose_list;
2608 }
2609
2610 void compose_entry_append(Compose *compose, const gchar *address,
2611                           ComposeEntryType type, ComposePrefType pref_type)
2612 {
2613         const gchar *header;
2614         gchar *cur, *begin;
2615         gboolean in_quote = FALSE;
2616         if (!address || *address == '\0') return;
2617
2618         switch (type) {
2619         case COMPOSE_CC:
2620                 header = N_("Cc:");
2621                 break;
2622         case COMPOSE_BCC:
2623                 header = N_("Bcc:");
2624                 break;
2625         case COMPOSE_REPLYTO:
2626                 header = N_("Reply-To:");
2627                 break;
2628         case COMPOSE_NEWSGROUPS:
2629                 header = N_("Newsgroups:");
2630                 break;
2631         case COMPOSE_FOLLOWUPTO:
2632                 header = N_( "Followup-To:");
2633                 break;
2634         case COMPOSE_INREPLYTO:
2635                 header = N_( "In-Reply-To:");
2636                 break;
2637         case COMPOSE_TO:
2638         default:
2639                 header = N_("To:");
2640                 break;
2641         }
2642         header = prefs_common_translated_header_name(header);
2643         
2644         cur = begin = (gchar *)address;
2645         
2646         /* we separate the line by commas, but not if we're inside a quoted
2647          * string */
2648         while (*cur != '\0') {
2649                 if (*cur == '"') 
2650                         in_quote = !in_quote;
2651                 if (*cur == ',' && !in_quote) {
2652                         gchar *tmp = g_strdup(begin);
2653                         gchar *o_tmp = tmp;
2654                         tmp[cur-begin]='\0';
2655                         cur++;
2656                         begin = cur;
2657                         while (*tmp == ' ' || *tmp == '\t')
2658                                 tmp++;
2659                         compose_add_header_entry(compose, header, tmp, pref_type);
2660                         g_free(o_tmp);
2661                         continue;
2662                 }
2663                 cur++;
2664         }
2665         if (begin < cur) {
2666                 gchar *tmp = g_strdup(begin);
2667                 gchar *o_tmp = tmp;
2668                 tmp[cur-begin]='\0';
2669                 while (*tmp == ' ' || *tmp == '\t')
2670                         tmp++;
2671                 compose_add_header_entry(compose, header, tmp, pref_type);
2672                 g_free(o_tmp);          
2673         }
2674 }
2675
2676 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2677 {
2678         GSList *h_list;
2679         GtkEntry *entry;
2680                 
2681         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2682                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2683                 if (gtk_entry_get_text(entry) && 
2684                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2685                                 gtk_widget_modify_base(
2686                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2687                                         GTK_STATE_NORMAL, &default_to_bgcolor);
2688                                 gtk_widget_modify_text(
2689                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2690                                         GTK_STATE_NORMAL, &default_to_color);
2691                 }
2692         }
2693 }
2694
2695 void compose_toolbar_cb(gint action, gpointer data)
2696 {
2697         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2698         Compose *compose = (Compose*)toolbar_item->parent;
2699         
2700         cm_return_if_fail(compose != NULL);
2701
2702         switch(action) {
2703         case A_SEND:
2704                 compose_send_cb(NULL, compose);
2705                 break;
2706         case A_SENDL:
2707                 compose_send_later_cb(NULL, compose);
2708                 break;
2709         case A_DRAFT:
2710                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2711                 break;
2712         case A_INSERT:
2713                 compose_insert_file_cb(NULL, compose);
2714                 break;
2715         case A_ATTACH:
2716                 compose_attach_cb(NULL, compose);
2717                 break;
2718         case A_SIG:
2719                 compose_insert_sig(compose, FALSE);
2720                 break;
2721         case A_REP_SIG:
2722                 compose_insert_sig(compose, TRUE);
2723                 break;
2724         case A_EXTEDITOR:
2725                 compose_ext_editor_cb(NULL, compose);
2726                 break;
2727         case A_LINEWRAP_CURRENT:
2728                 compose_beautify_paragraph(compose, NULL, TRUE);
2729                 break;
2730         case A_LINEWRAP_ALL:
2731                 compose_wrap_all_full(compose, TRUE);
2732                 break;
2733         case A_ADDRBOOK:
2734                 compose_address_cb(NULL, compose);
2735                 break;
2736 #ifdef USE_ENCHANT
2737         case A_CHECK_SPELLING:
2738                 compose_check_all(NULL, compose);
2739                 break;
2740 #endif
2741         default:
2742                 break;
2743         }
2744 }
2745
2746 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2747 {
2748         gchar *to = NULL;
2749         gchar *cc = NULL;
2750         gchar *bcc = NULL;
2751         gchar *subject = NULL;
2752         gchar *body = NULL;
2753         gchar *temp = NULL;
2754         gsize  len = 0;
2755         gchar **attach = NULL;
2756         gchar *inreplyto = NULL;
2757         MailField mfield = NO_FIELD_PRESENT;
2758
2759         /* get mailto parts but skip from */
2760         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2761
2762         if (to) {
2763                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2764                 mfield = TO_FIELD_PRESENT;
2765         }
2766         if (cc)
2767                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2768         if (bcc)
2769                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2770         if (subject) {
2771                 if (!g_utf8_validate (subject, -1, NULL)) {
2772                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2773                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2774                         g_free(temp);
2775                 } else {
2776                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2777                 }
2778                 mfield = SUBJECT_FIELD_PRESENT;
2779         }
2780         if (body) {
2781                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2782                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2783                 GtkTextMark *mark;
2784                 GtkTextIter iter;
2785                 gboolean prev_autowrap = compose->autowrap;
2786
2787                 compose->autowrap = FALSE;
2788
2789                 mark = gtk_text_buffer_get_insert(buffer);
2790                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2791
2792                 if (!g_utf8_validate (body, -1, NULL)) {
2793                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2794                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2795                         g_free(temp);
2796                 } else {
2797                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2798                 }
2799                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2800
2801                 compose->autowrap = prev_autowrap;
2802                 if (compose->autowrap)
2803                         compose_wrap_all(compose);
2804                 mfield = BODY_FIELD_PRESENT;
2805         }
2806
2807         if (attach) {
2808                 gint i = 0, att = 0;
2809                 gchar *warn_files = NULL;
2810                 while (attach[i] != NULL) {
2811                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2812                         if (utf8_filename) {
2813                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2814                                         gchar *tmp = g_strdup_printf("%s%s\n",
2815                                                         warn_files?warn_files:"",
2816                                                         utf8_filename);
2817                                         g_free(warn_files);
2818                                         warn_files = tmp;
2819                                         att++;
2820                                 }
2821                                 g_free(utf8_filename);
2822                         } else {
2823                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2824                         }
2825                         i++;
2826                 }
2827                 if (warn_files) {
2828                         alertpanel_notice(ngettext(
2829                         "The following file has been attached: \n%s",
2830                         "The following files have been attached: \n%s", att), warn_files);
2831                         g_free(warn_files);
2832                 }
2833         }
2834         if (inreplyto)
2835                 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2836
2837         g_free(to);
2838         g_free(cc);
2839         g_free(bcc);
2840         g_free(subject);
2841         g_free(body);
2842         g_strfreev(attach);
2843         g_free(inreplyto);
2844         
2845         return mfield;
2846 }
2847
2848 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2849 {
2850         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2851                                        {"Cc:",          NULL, TRUE},
2852                                        {"References:",  NULL, FALSE},
2853                                        {"Bcc:",         NULL, TRUE},
2854                                        {"Newsgroups:",  NULL, TRUE},
2855                                        {"Followup-To:", NULL, TRUE},
2856                                        {"List-Post:",   NULL, FALSE},
2857                                        {"X-Priority:",  NULL, FALSE},
2858                                        {NULL,           NULL, FALSE}};
2859
2860         enum
2861         {
2862                 H_REPLY_TO      = 0,
2863                 H_CC            = 1,
2864                 H_REFERENCES    = 2,
2865                 H_BCC           = 3,
2866                 H_NEWSGROUPS    = 4,
2867                 H_FOLLOWUP_TO   = 5,
2868                 H_LIST_POST     = 6,
2869                 H_X_PRIORITY    = 7
2870         };
2871
2872         FILE *fp;
2873
2874         cm_return_val_if_fail(msginfo != NULL, -1);
2875
2876         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2877         procheader_get_header_fields(fp, hentry);
2878         fclose(fp);
2879
2880         if (hentry[H_REPLY_TO].body != NULL) {
2881                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2882                         compose->replyto =
2883                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2884                                                    NULL, TRUE);
2885                 }
2886                 g_free(hentry[H_REPLY_TO].body);
2887                 hentry[H_REPLY_TO].body = NULL;
2888         }
2889         if (hentry[H_CC].body != NULL) {
2890                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2891                 g_free(hentry[H_CC].body);
2892                 hentry[H_CC].body = NULL;
2893         }
2894         if (hentry[H_REFERENCES].body != NULL) {
2895                 if (compose->mode == COMPOSE_REEDIT)
2896                         compose->references = hentry[H_REFERENCES].body;
2897                 else {
2898                         compose->references = compose_parse_references
2899                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2900                         g_free(hentry[H_REFERENCES].body);
2901                 }
2902                 hentry[H_REFERENCES].body = NULL;
2903         }
2904         if (hentry[H_BCC].body != NULL) {
2905                 if (compose->mode == COMPOSE_REEDIT)
2906                         compose->bcc =
2907                                 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2908                 g_free(hentry[H_BCC].body);
2909                 hentry[H_BCC].body = NULL;
2910         }
2911         if (hentry[H_NEWSGROUPS].body != NULL) {
2912                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2913                 hentry[H_NEWSGROUPS].body = NULL;
2914         }
2915         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2916                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2917                         compose->followup_to =
2918                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2919                                                    NULL, TRUE);
2920                 }
2921                 g_free(hentry[H_FOLLOWUP_TO].body);
2922                 hentry[H_FOLLOWUP_TO].body = NULL;
2923         }
2924         if (hentry[H_LIST_POST].body != NULL) {
2925                 gchar *to = NULL, *start = NULL;
2926
2927                 extract_address(hentry[H_LIST_POST].body);
2928                 if (hentry[H_LIST_POST].body[0] != '\0') {
2929                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2930                         
2931                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2932                                         NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2933
2934                         if (to) {
2935                                 g_free(compose->ml_post);
2936                                 compose->ml_post = to;
2937                         }
2938                 }
2939                 g_free(hentry[H_LIST_POST].body);
2940                 hentry[H_LIST_POST].body = NULL;
2941         }
2942
2943         /* CLAWS - X-Priority */
2944         if (compose->mode == COMPOSE_REEDIT)
2945                 if (hentry[H_X_PRIORITY].body != NULL) {
2946                         gint priority;
2947                         
2948                         priority = atoi(hentry[H_X_PRIORITY].body);
2949                         g_free(hentry[H_X_PRIORITY].body);
2950                         
2951                         hentry[H_X_PRIORITY].body = NULL;
2952                         
2953                         if (priority < PRIORITY_HIGHEST || 
2954                             priority > PRIORITY_LOWEST)
2955                                 priority = PRIORITY_NORMAL;
2956                         
2957                         compose->priority =  priority;
2958                 }
2959  
2960         if (compose->mode == COMPOSE_REEDIT) {
2961                 if (msginfo->inreplyto && *msginfo->inreplyto)
2962                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2963
2964                 if (msginfo->msgid && *msginfo->msgid)
2965                         compose->msgid = g_strdup(msginfo->msgid);
2966         } else {
2967                 if (msginfo->msgid && *msginfo->msgid)
2968                         compose->inreplyto = g_strdup(msginfo->msgid);
2969
2970                 if (!compose->references) {
2971                         if (msginfo->msgid && *msginfo->msgid) {
2972                                 if (msginfo->inreplyto && *msginfo->inreplyto)
2973                                         compose->references =
2974                                                 g_strdup_printf("<%s>\n\t<%s>",
2975                                                                 msginfo->inreplyto,
2976                                                                 msginfo->msgid);
2977                                 else
2978                                         compose->references =
2979                                                 g_strconcat("<", msginfo->msgid, ">",
2980                                                             NULL);
2981                         } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2982                                 compose->references =
2983                                         g_strconcat("<", msginfo->inreplyto, ">",
2984                                                     NULL);
2985                         }
2986                 }
2987         }
2988
2989         return 0;
2990 }
2991
2992 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2993 {
2994         FILE *fp;
2995         HeaderEntry *he;
2996
2997         cm_return_val_if_fail(msginfo != NULL, -1);
2998
2999         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3000         procheader_get_header_fields(fp, entries);
3001         fclose(fp);
3002
3003         he = entries;
3004         while (he != NULL && he->name != NULL) {
3005                 GtkTreeIter iter;
3006                 GtkListStore *model = NULL;
3007
3008                 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3009                 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3010                 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3011                 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3012                 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3013                 ++he;
3014         }
3015
3016         return 0;
3017 }
3018
3019 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3020 {
3021         GSList *ref_id_list, *cur;
3022         GString *new_ref;
3023         gchar *new_ref_str;
3024
3025         ref_id_list = references_list_append(NULL, ref);
3026         if (!ref_id_list) return NULL;
3027         if (msgid && *msgid)
3028                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3029
3030         for (;;) {
3031                 gint len = 0;
3032
3033                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3034                         /* "<" + Message-ID + ">" + CR+LF+TAB */
3035                         len += strlen((gchar *)cur->data) + 5;
3036
3037                 if (len > MAX_REFERENCES_LEN) {
3038                         /* remove second message-ID */
3039                         if (ref_id_list && ref_id_list->next &&
3040                             ref_id_list->next->next) {
3041                                 g_free(ref_id_list->next->data);
3042                                 ref_id_list = g_slist_remove
3043                                         (ref_id_list, ref_id_list->next->data);
3044                         } else {
3045                                 slist_free_strings_full(ref_id_list);
3046                                 return NULL;
3047                         }
3048                 } else
3049                         break;
3050         }
3051
3052         new_ref = g_string_new("");
3053         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3054                 if (new_ref->len > 0)
3055                         g_string_append(new_ref, "\n\t");
3056                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3057         }
3058
3059         slist_free_strings_full(ref_id_list);
3060
3061         new_ref_str = new_ref->str;
3062         g_string_free(new_ref, FALSE);
3063
3064         return new_ref_str;
3065 }
3066
3067 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3068                                 const gchar *fmt, const gchar *qmark,
3069                                 const gchar *body, gboolean rewrap,
3070                                 gboolean need_unescape,
3071                                 const gchar *err_msg)
3072 {
3073         MsgInfo* dummyinfo = NULL;
3074         gchar *quote_str = NULL;
3075         gchar *buf;
3076         gboolean prev_autowrap;
3077         const gchar *trimmed_body = body;
3078         gint cursor_pos = -1;
3079         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3080         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3081         GtkTextIter iter;
3082         GtkTextMark *mark;
3083         
3084
3085         SIGNAL_BLOCK(buffer);
3086
3087         if (!msginfo) {
3088                 dummyinfo = compose_msginfo_new_from_compose(compose);
3089                 msginfo = dummyinfo;
3090         }
3091
3092         if (qmark != NULL) {
3093 #ifdef USE_ENCHANT
3094                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3095                                 compose->gtkaspell);
3096 #else
3097                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3098 #endif
3099                 quote_fmt_scan_string(qmark);
3100                 quote_fmt_parse();
3101
3102                 buf = quote_fmt_get_buffer();
3103                 if (buf == NULL)
3104                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3105                 else
3106                         Xstrdup_a(quote_str, buf, goto error)
3107         }
3108
3109         if (fmt && *fmt != '\0') {
3110
3111                 if (trimmed_body)
3112                         while (*trimmed_body == '\n')
3113                                 trimmed_body++;
3114
3115 #ifdef USE_ENCHANT
3116                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3117                                 compose->gtkaspell);
3118 #else
3119                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3120 #endif
3121                 if (need_unescape) {
3122                         gchar *tmp = NULL;
3123
3124                         /* decode \-escape sequences in the internal representation of the quote format */
3125                         tmp = g_malloc(strlen(fmt)+1);
3126                         pref_get_unescaped_pref(tmp, fmt);
3127                         quote_fmt_scan_string(tmp);
3128                         quote_fmt_parse();
3129                         g_free(tmp);
3130                 } else {
3131                         quote_fmt_scan_string(fmt);
3132                         quote_fmt_parse();
3133                 }
3134
3135                 buf = quote_fmt_get_buffer();
3136                 if (buf == NULL) {
3137                         gint line = quote_fmt_get_line();
3138                         alertpanel_error(err_msg, line);
3139                         goto error;
3140                 }
3141         } else
3142                 buf = "";
3143
3144         prev_autowrap = compose->autowrap;
3145         compose->autowrap = FALSE;
3146
3147         mark = gtk_text_buffer_get_insert(buffer);
3148         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3149         if (g_utf8_validate(buf, -1, NULL)) { 
3150                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3151         } else {
3152                 gchar *tmpout = NULL;
3153                 tmpout = conv_codeset_strdup
3154                         (buf, conv_get_locale_charset_str_no_utf8(),
3155                          CS_INTERNAL);
3156                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3157                         g_free(tmpout);
3158                         tmpout = g_malloc(strlen(buf)*2+1);
3159                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3160                 }
3161                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3162                 g_free(tmpout);
3163         }
3164
3165         cursor_pos = quote_fmt_get_cursor_pos();
3166         if (cursor_pos == -1)
3167                 cursor_pos = gtk_text_iter_get_offset(&iter);
3168         compose->set_cursor_pos = cursor_pos;
3169
3170         gtk_text_buffer_get_start_iter(buffer, &iter);
3171         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3172         gtk_text_buffer_place_cursor(buffer, &iter);
3173
3174         compose->autowrap = prev_autowrap;
3175         if (compose->autowrap && rew