Merge branch 'master' of ssh+git://git.claws-mail.org/home/git/claws
[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_header_bgcolor = {
187         (gulong)0,
188         (gushort)0,
189         (gushort)0,
190         (gushort)0
191 };
192
193 static GdkColor default_header_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_indicate      (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_find_account       (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 && !(account = compose_find_account(msginfo)))
1774                 account = cur_account;
1775
1776         if (!prefs_common.forward_as_attachment)
1777                 mode = COMPOSE_FORWARD_INLINE;
1778         else
1779                 mode = COMPOSE_FORWARD;
1780         compose = compose_create(account, msginfo->folder, mode, batch);
1781
1782         compose->updating = TRUE;
1783         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1784         if (!compose->fwdinfo)
1785                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1786
1787         compose_extract_original_charset(compose);
1788
1789         if (msginfo->subject && *msginfo->subject) {
1790                 gchar *buf, *buf2, *p;
1791
1792                 buf = p = g_strdup(msginfo->subject);
1793                 p += subject_get_prefix_length(p);
1794                 memmove(buf, p, strlen(p) + 1);
1795
1796                 buf2 = g_strdup_printf("Fw: %s", buf);
1797                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1798                 
1799                 g_free(buf);
1800                 g_free(buf2);
1801         }
1802
1803         /* override from name according to folder properties */
1804         if (msginfo->folder && msginfo->folder->prefs &&
1805                 msginfo->folder->prefs->forward_with_format &&
1806                 msginfo->folder->prefs->forward_override_from_format &&
1807                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1808
1809                 gchar *tmp = NULL;
1810                 gchar *buf = NULL;
1811                 MsgInfo *full_msginfo = NULL;
1812
1813                 if (!as_attach)
1814                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1815                 if (!full_msginfo)
1816                         full_msginfo = procmsg_msginfo_copy(msginfo);
1817
1818                 /* decode \-escape sequences in the internal representation of the quote format */
1819                 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1820                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1821
1822 #ifdef USE_ENCHANT
1823                 gtkaspell_block_check(compose->gtkaspell);
1824                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1825                                 compose->gtkaspell);
1826 #else
1827                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1828 #endif
1829                 quote_fmt_scan_string(tmp);
1830                 quote_fmt_parse();
1831
1832                 buf = quote_fmt_get_buffer();
1833                 if (buf == NULL)
1834                         alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1835                 else
1836                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1837                 quote_fmt_reset_vartable();
1838
1839                 g_free(tmp);
1840                 procmsg_msginfo_free(&full_msginfo);
1841         }
1842
1843         textview = GTK_TEXT_VIEW(compose->text);
1844         textbuf = gtk_text_view_get_buffer(textview);
1845         compose_create_tags(textview, compose);
1846         
1847         undo_block(compose->undostruct);
1848         if (as_attach) {
1849                 gchar *msgfile;
1850
1851                 msgfile = procmsg_get_message_file(msginfo);
1852                 if (!is_file_exist(msgfile))
1853                         g_warning("%s: file does not exist", msgfile);
1854                 else
1855                         compose_attach_append(compose, msgfile, msgfile,
1856                                               "message/rfc822", NULL);
1857
1858                 g_free(msgfile);
1859         } else {
1860                 const gchar *qmark = NULL;
1861                 const gchar *body_fmt = NULL;
1862                 MsgInfo *full_msginfo;
1863
1864                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1865                 if (!full_msginfo)
1866                         full_msginfo = procmsg_msginfo_copy(msginfo);
1867
1868                 /* use the forward format of folder (if enabled), or the account's one
1869                    (if enabled) or fallback to the global forward format, which is always
1870                    enabled (even if empty), and use the relevant quotemark */
1871                 if (msginfo->folder && msginfo->folder->prefs &&
1872                                 msginfo->folder->prefs->forward_with_format) {
1873                         qmark = msginfo->folder->prefs->forward_quotemark;
1874                         body_fmt = msginfo->folder->prefs->forward_body_format;
1875
1876                 } else if (account->forward_with_format) {
1877                         qmark = account->forward_quotemark;
1878                         body_fmt = account->forward_body_format;
1879
1880                 } else {
1881                         qmark = prefs_common.fw_quotemark;
1882                         if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1883                                 body_fmt = gettext(prefs_common.fw_quotefmt);
1884                         else
1885                                 body_fmt = "";
1886                 }
1887
1888                 /* empty quotemark is not allowed */
1889                 if (qmark == NULL || *qmark == '\0')
1890                         qmark = "> ";
1891
1892                 compose_quote_fmt(compose, full_msginfo,
1893                                   body_fmt, qmark, body, FALSE, TRUE,
1894                                           _("The body of the \"Forward\" template has an error at line %d."));
1895                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1896                 quote_fmt_reset_vartable();
1897                 compose_attach_parts(compose, msginfo);
1898
1899                 procmsg_msginfo_free(&full_msginfo);
1900         }
1901
1902         SIGNAL_BLOCK(textbuf);
1903
1904         if (account->auto_sig)
1905                 compose_insert_sig(compose, FALSE);
1906
1907         compose_wrap_all(compose);
1908
1909 #ifdef USE_ENCHANT
1910         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1911                 gtkaspell_highlight_all(compose->gtkaspell);
1912         gtkaspell_unblock_check(compose->gtkaspell);
1913 #endif
1914         SIGNAL_UNBLOCK(textbuf);
1915         
1916         cursor_pos = quote_fmt_get_cursor_pos();
1917         if (cursor_pos == -1)
1918                 gtk_widget_grab_focus(compose->header_last->entry);
1919         else
1920                 gtk_widget_grab_focus(compose->text);
1921
1922         if (!no_extedit && prefs_common.auto_exteditor)
1923                 compose_exec_ext_editor(compose);
1924         
1925         /*save folder*/
1926         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1927                 gchar *folderidentifier;
1928
1929                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1930                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1931                 compose_set_save_to(compose, folderidentifier);
1932                 g_free(folderidentifier);
1933         }
1934
1935         undo_unblock(compose->undostruct);
1936         
1937         compose->modified = FALSE;
1938         compose_set_title(compose);
1939
1940         compose->updating = FALSE;
1941         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1942         SCROLL_TO_CURSOR(compose);
1943
1944         if (compose->deferred_destroy) {
1945                 compose_destroy(compose);
1946                 return NULL;
1947         }
1948
1949         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1950
1951         return compose;
1952 }
1953
1954 #undef INSERT_FW_HEADER
1955
1956 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1957 {
1958         Compose *compose;
1959         GtkTextView *textview;
1960         GtkTextBuffer *textbuf;
1961         GtkTextIter iter;
1962         GSList *msginfo;
1963         gchar *msgfile;
1964         gboolean single_mail = TRUE;
1965         
1966         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1967
1968         if (g_slist_length(msginfo_list) > 1)
1969                 single_mail = FALSE;
1970
1971         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1972                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1973                         return NULL;
1974
1975         /* guess account from first selected message */
1976         if (!account && 
1977             !(account = compose_find_account(msginfo_list->data)))
1978                 account = cur_account;
1979
1980         cm_return_val_if_fail(account != NULL, NULL);
1981
1982         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1983                 if (msginfo->data) {
1984                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1985                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1986                 }
1987         }
1988
1989         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1990                 g_warning("no msginfo_list");
1991                 return NULL;
1992         }
1993
1994         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1995
1996         compose->updating = TRUE;
1997
1998         /* override from name according to folder properties */
1999         if (msginfo_list->data) {
2000                 MsgInfo *msginfo = msginfo_list->data;
2001
2002                 if (msginfo->folder && msginfo->folder->prefs &&
2003                         msginfo->folder->prefs->forward_with_format &&
2004                         msginfo->folder->prefs->forward_override_from_format &&
2005                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
2006
2007                         gchar *tmp = NULL;
2008                         gchar *buf = NULL;
2009
2010                         /* decode \-escape sequences in the internal representation of the quote format */
2011                         tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2012                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2013
2014 #ifdef USE_ENCHANT
2015                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2016                                         compose->gtkaspell);
2017 #else
2018                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2019 #endif
2020                         quote_fmt_scan_string(tmp);
2021                         quote_fmt_parse();
2022
2023                         buf = quote_fmt_get_buffer();
2024                         if (buf == NULL)
2025                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2026                         else
2027                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2028                         quote_fmt_reset_vartable();
2029
2030                         g_free(tmp);
2031                 }
2032         }
2033
2034         textview = GTK_TEXT_VIEW(compose->text);
2035         textbuf = gtk_text_view_get_buffer(textview);
2036         compose_create_tags(textview, compose);
2037         
2038         undo_block(compose->undostruct);
2039         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2040                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2041
2042                 if (!is_file_exist(msgfile))
2043                         g_warning("%s: file does not exist", msgfile);
2044                 else
2045                         compose_attach_append(compose, msgfile, msgfile,
2046                                 "message/rfc822", NULL);
2047                 g_free(msgfile);
2048         }
2049         
2050         if (single_mail) {
2051                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2052                 if (info->subject && *info->subject) {
2053                         gchar *buf, *buf2, *p;
2054
2055                         buf = p = g_strdup(info->subject);
2056                         p += subject_get_prefix_length(p);
2057                         memmove(buf, p, strlen(p) + 1);
2058
2059                         buf2 = g_strdup_printf("Fw: %s", buf);
2060                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2061
2062                         g_free(buf);
2063                         g_free(buf2);
2064                 }
2065         } else {
2066                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2067                         _("Fw: multiple emails"));
2068         }
2069
2070         SIGNAL_BLOCK(textbuf);
2071         
2072         if (account->auto_sig)
2073                 compose_insert_sig(compose, FALSE);
2074
2075         compose_wrap_all(compose);
2076
2077         SIGNAL_UNBLOCK(textbuf);
2078         
2079         gtk_text_buffer_get_start_iter(textbuf, &iter);
2080         gtk_text_buffer_place_cursor(textbuf, &iter);
2081
2082         if (prefs_common.auto_exteditor)
2083                 compose_exec_ext_editor(compose);
2084
2085         gtk_widget_grab_focus(compose->header_last->entry);
2086         undo_unblock(compose->undostruct);
2087         compose->modified = FALSE;
2088         compose_set_title(compose);
2089
2090         compose->updating = FALSE;
2091         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2092         SCROLL_TO_CURSOR(compose);
2093
2094         if (compose->deferred_destroy) {
2095                 compose_destroy(compose);
2096                 return NULL;
2097         }
2098
2099         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2100
2101         return compose;
2102 }
2103
2104 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2105 {
2106         GtkTextIter start = *iter;
2107         GtkTextIter end_iter;
2108         int start_pos = gtk_text_iter_get_offset(&start);
2109         gchar *str = NULL;
2110         if (!compose->account->sig_sep)
2111                 return FALSE;
2112         
2113         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2114                 start_pos+strlen(compose->account->sig_sep));
2115
2116         /* check sig separator */
2117         str = gtk_text_iter_get_text(&start, &end_iter);
2118         if (!strcmp(str, compose->account->sig_sep)) {
2119                 gchar *tmp = NULL;
2120                 /* check end of line (\n) */
2121                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2122                         start_pos+strlen(compose->account->sig_sep));
2123                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2124                         start_pos+strlen(compose->account->sig_sep)+1);
2125                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2126                 if (!strcmp(tmp,"\n")) {
2127                         g_free(str);
2128                         g_free(tmp);
2129                         return TRUE;
2130                 }
2131                 g_free(tmp);    
2132         }
2133         g_free(str);
2134
2135         return FALSE;
2136 }
2137
2138 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2139 {
2140         FolderUpdateData *hookdata = (FolderUpdateData *)source;
2141         Compose *compose = (Compose *)data;
2142         FolderItem *old_item = NULL;
2143         FolderItem *new_item = NULL;
2144         gchar *old_id, *new_id;
2145
2146         if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2147          && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2148                 return FALSE;
2149
2150         old_item = hookdata->item;
2151         new_item = hookdata->item2;
2152
2153         old_id = folder_item_get_identifier(old_item);
2154         new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2155
2156         if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2157                 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2158                 compose->targetinfo->folder = new_item;
2159         }
2160
2161         if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2162                 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2163                 compose->replyinfo->folder = new_item;
2164         }
2165
2166         if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2167                 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2168                 compose->fwdinfo->folder = new_item;
2169         }
2170
2171         g_free(old_id);
2172         g_free(new_id);
2173         return FALSE;
2174 }
2175
2176 static void compose_colorize_signature(Compose *compose)
2177 {
2178         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2179         GtkTextIter iter;
2180         GtkTextIter end_iter;
2181         gtk_text_buffer_get_start_iter(buffer, &iter);
2182         while (gtk_text_iter_forward_line(&iter))
2183                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2184                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2185                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2186                 }
2187 }
2188
2189 #define BLOCK_WRAP() {                                                  \
2190         prev_autowrap = compose->autowrap;                              \
2191         buffer = gtk_text_view_get_buffer(                              \
2192                                         GTK_TEXT_VIEW(compose->text));  \
2193         compose->autowrap = FALSE;                                      \
2194                                                                         \
2195         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2196                                 G_CALLBACK(compose_changed_cb),         \
2197                                 compose);                               \
2198         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2199                                 G_CALLBACK(text_inserted),              \
2200                                 compose);                               \
2201 }
2202 #define UNBLOCK_WRAP() {                                                        \
2203         compose->autowrap = prev_autowrap;                                      \
2204         if (compose->autowrap) {                                                \
2205                 gint old = compose->draft_timeout_tag;                          \
2206                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;   \
2207                 compose_wrap_all(compose);                                      \
2208                 compose->draft_timeout_tag = old;                               \
2209         }                                                                       \
2210                                                                                 \
2211         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
2212                                 G_CALLBACK(compose_changed_cb),                 \
2213                                 compose);                                       \
2214         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
2215                                 G_CALLBACK(text_inserted),                      \
2216                                 compose);                                       \
2217 }
2218
2219 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2220 {
2221         Compose *compose = NULL;
2222         PrefsAccount *account = NULL;
2223         GtkTextView *textview;
2224         GtkTextBuffer *textbuf;
2225         GtkTextMark *mark;
2226         GtkTextIter iter;
2227         FILE *fp;
2228         gchar buf[BUFFSIZE];
2229         gboolean use_signing = FALSE;
2230         gboolean use_encryption = FALSE;
2231         gchar *privacy_system = NULL;
2232         int priority = PRIORITY_NORMAL;
2233         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2234         gboolean autowrap = prefs_common.autowrap;
2235         gboolean autoindent = prefs_common.auto_indent;
2236         HeaderEntry *manual_headers = NULL;
2237
2238         cm_return_val_if_fail(msginfo != NULL, NULL);
2239         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2240
2241         if (compose_put_existing_to_front(msginfo)) {
2242                 return NULL;
2243         }
2244
2245         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2246             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2247             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2248                 gchar queueheader_buf[BUFFSIZE];
2249                 gint id, param;
2250
2251                 /* Select Account from queue headers */
2252                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2253                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2254                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2255                         account = account_find_from_id(id);
2256                 }
2257                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2258                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2259                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2260                         account = account_find_from_id(id);
2261                 }
2262                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2263                                              sizeof(queueheader_buf), "NAID:")) {
2264                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2265                         account = account_find_from_id(id);
2266                 }
2267                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2268                                                     sizeof(queueheader_buf), "MAID:")) {
2269                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2270                         account = account_find_from_id(id);
2271                 }
2272                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2273                                                                 sizeof(queueheader_buf), "S:")) {
2274                         account = account_find_from_address(queueheader_buf, FALSE);
2275                 }
2276                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2277                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
2278                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2279                         use_signing = param;
2280                         
2281                 }
2282                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2283                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2284                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2285                         use_signing = param;
2286                         
2287                 }
2288                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2289                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2290                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2291                         use_encryption = param;
2292                 }
2293                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2294                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2295                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2296                         use_encryption = param;
2297                 }
2298                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2299                                              sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2300                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2301                         autowrap = param;
2302                 }
2303                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2304                                              sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2305                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2306                         autoindent = param;
2307                 }
2308                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2309                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2310                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2311                 }
2312                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2313                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2314                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2315                 }
2316                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2317                                              sizeof(queueheader_buf), "X-Priority: ")) {
2318                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2319                         priority = param;
2320                 }
2321                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2322                                              sizeof(queueheader_buf), "RMID:")) {
2323                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2324                         if (tokens[0] && tokens[1] && tokens[2]) {
2325                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2326                                 if (orig_item != NULL) {
2327                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2328                                 }
2329                         }
2330                         g_strfreev(tokens);
2331                 }
2332                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2333                                              sizeof(queueheader_buf), "FMID:")) {
2334                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2335                         if (tokens[0] && tokens[1] && tokens[2]) {
2336                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2337                                 if (orig_item != NULL) {
2338                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2339                                 }
2340                         }
2341                         g_strfreev(tokens);
2342                 }
2343                 /* Get manual headers */
2344                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2345                         gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2346                         if (*listmh != '\0') {
2347                                 debug_print("Got manual headers: %s\n", listmh);
2348                                 manual_headers = procheader_entries_from_str(listmh);
2349                         }
2350                         g_free(listmh);
2351                 }
2352         } else {
2353                 account = msginfo->folder->folder->account;
2354         }
2355
2356         if (!account && prefs_common.reedit_account_autosel) {
2357                 gchar from[BUFFSIZE];
2358                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2359                         extract_address(from);
2360                         account = account_find_from_address(from, FALSE);
2361                 }
2362         }
2363         if (!account) {
2364                 account = cur_account;
2365         }
2366         cm_return_val_if_fail(account != NULL, NULL);
2367
2368         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2369
2370         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2371         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2372         compose->autowrap = autowrap;
2373         compose->replyinfo = replyinfo;
2374         compose->fwdinfo = fwdinfo;
2375
2376         compose->updating = TRUE;
2377         compose->priority = priority;
2378
2379         if (privacy_system != NULL) {
2380                 compose->privacy_system = privacy_system;
2381                 compose_use_signing(compose, use_signing);
2382                 compose_use_encryption(compose, use_encryption);
2383                 compose_update_privacy_system_menu_item(compose, FALSE);
2384         } else {
2385                 activate_privacy_system(compose, account, FALSE);
2386         }
2387
2388         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2389
2390         compose_extract_original_charset(compose);
2391
2392         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2393             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2394             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2395                 gchar queueheader_buf[BUFFSIZE];
2396
2397                 /* Set message save folder */
2398                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2399                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2400                         compose_set_save_to(compose, &queueheader_buf[4]);
2401                 }
2402                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2403                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2404                         if (active) {
2405                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2406                         }
2407                 }
2408         }
2409         
2410         if (compose_parse_header(compose, msginfo) < 0) {
2411                 compose->updating = FALSE;
2412                 compose_destroy(compose);
2413                 return NULL;
2414         }
2415         compose_reedit_set_entry(compose, msginfo);
2416
2417         textview = GTK_TEXT_VIEW(compose->text);
2418         textbuf = gtk_text_view_get_buffer(textview);
2419         compose_create_tags(textview, compose);
2420
2421         mark = gtk_text_buffer_get_insert(textbuf);
2422         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2423
2424         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2425                                         G_CALLBACK(compose_changed_cb),
2426                                         compose);
2427         
2428         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2429                 fp = procmime_get_first_encrypted_text_content(msginfo);
2430                 if (fp) {
2431                         compose_force_encryption(compose, account, TRUE, NULL);
2432                 }
2433         } else {
2434                 fp = procmime_get_first_text_content(msginfo);
2435         }
2436         if (fp == NULL) {
2437                 g_warning("Can't get text part");
2438         }
2439
2440         if (fp != NULL) {
2441                 gboolean prev_autowrap;
2442                 GtkTextBuffer *buffer;
2443                 BLOCK_WRAP();
2444                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2445                         strcrchomp(buf);
2446                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2447                 }
2448                 UNBLOCK_WRAP();
2449                 fclose(fp);
2450         }
2451         
2452         compose_attach_parts(compose, msginfo);
2453
2454         compose_colorize_signature(compose);
2455
2456         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2457                                         G_CALLBACK(compose_changed_cb),
2458                                         compose);
2459
2460         if (manual_headers != NULL) {
2461                 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2462                         procheader_entries_free(manual_headers);
2463                         compose->updating = FALSE;
2464                         compose_destroy(compose);
2465                         return NULL;
2466                 }
2467                 procheader_entries_free(manual_headers);
2468         }
2469
2470         gtk_widget_grab_focus(compose->text);
2471
2472         if (prefs_common.auto_exteditor) {
2473                 compose_exec_ext_editor(compose);
2474         }
2475         compose->modified = FALSE;
2476         compose_set_title(compose);
2477
2478         compose->updating = FALSE;
2479         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2480         SCROLL_TO_CURSOR(compose);
2481
2482         if (compose->deferred_destroy) {
2483                 compose_destroy(compose);
2484                 return NULL;
2485         }
2486         
2487         compose->sig_str = account_get_signature_str(compose->account);
2488         
2489         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2490
2491         return compose;
2492 }
2493
2494 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2495                                                  gboolean batch)
2496 {
2497         Compose *compose;
2498         gchar *filename;
2499         FolderItem *item;
2500
2501         cm_return_val_if_fail(msginfo != NULL, NULL);
2502
2503         if (!account)
2504                 account = account_get_reply_account(msginfo,
2505                                         prefs_common.reply_account_autosel);
2506         cm_return_val_if_fail(account != NULL, NULL);
2507
2508         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2509
2510         compose->updating = TRUE;
2511
2512         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2513         compose->replyinfo = NULL;
2514         compose->fwdinfo = NULL;
2515
2516         compose_show_first_last_header(compose, TRUE);
2517
2518         gtk_widget_grab_focus(compose->header_last->entry);
2519
2520         filename = procmsg_get_message_file(msginfo);
2521
2522         if (filename == NULL) {
2523                 compose->updating = FALSE;
2524                 compose_destroy(compose);
2525
2526                 return NULL;
2527         }
2528
2529         compose->redirect_filename = filename;
2530         
2531         /* Set save folder */
2532         item = msginfo->folder;
2533         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2534                 gchar *folderidentifier;
2535
2536                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2537                 folderidentifier = folder_item_get_identifier(item);
2538                 compose_set_save_to(compose, folderidentifier);
2539                 g_free(folderidentifier);
2540         }
2541
2542         compose_attach_parts(compose, msginfo);
2543
2544         if (msginfo->subject)
2545                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2546                                    msginfo->subject);
2547         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2548
2549         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2550                                           _("The body of the \"Redirect\" template has an error at line %d."));
2551         quote_fmt_reset_vartable();
2552         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2553
2554         compose_colorize_signature(compose);
2555
2556         
2557         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2558         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2559         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2560
2561         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2562         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2563         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2564         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2565         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2566         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2567         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2568         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2569         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2570         
2571         if (compose->toolbar->draft_btn)
2572                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2573         if (compose->toolbar->insert_btn)
2574                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2575         if (compose->toolbar->attach_btn)
2576                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2577         if (compose->toolbar->sig_btn)
2578                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2579         if (compose->toolbar->exteditor_btn)
2580                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2581         if (compose->toolbar->linewrap_current_btn)
2582                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2583         if (compose->toolbar->linewrap_all_btn)
2584                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2585
2586         compose->modified = FALSE;
2587         compose_set_title(compose);
2588         compose->updating = FALSE;
2589         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2590         SCROLL_TO_CURSOR(compose);
2591
2592         if (compose->deferred_destroy) {
2593                 compose_destroy(compose);
2594                 return NULL;
2595         }
2596         
2597         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2598
2599         return compose;
2600 }
2601
2602 const GList *compose_get_compose_list(void)
2603 {
2604         return compose_list;
2605 }
2606
2607 void compose_entry_append(Compose *compose, const gchar *address,
2608                           ComposeEntryType type, ComposePrefType pref_type)
2609 {
2610         const gchar *header;
2611         gchar *cur, *begin;
2612         gboolean in_quote = FALSE;
2613         if (!address || *address == '\0') return;
2614
2615         switch (type) {
2616         case COMPOSE_CC:
2617                 header = N_("Cc:");
2618                 break;
2619         case COMPOSE_BCC:
2620                 header = N_("Bcc:");
2621                 break;
2622         case COMPOSE_REPLYTO:
2623                 header = N_("Reply-To:");
2624                 break;
2625         case COMPOSE_NEWSGROUPS:
2626                 header = N_("Newsgroups:");
2627                 break;
2628         case COMPOSE_FOLLOWUPTO:
2629                 header = N_( "Followup-To:");
2630                 break;
2631         case COMPOSE_INREPLYTO:
2632                 header = N_( "In-Reply-To:");
2633                 break;
2634         case COMPOSE_TO:
2635         default:
2636                 header = N_("To:");
2637                 break;
2638         }
2639         header = prefs_common_translated_header_name(header);
2640         
2641         cur = begin = (gchar *)address;
2642         
2643         /* we separate the line by commas, but not if we're inside a quoted
2644          * string */
2645         while (*cur != '\0') {
2646                 if (*cur == '"') 
2647                         in_quote = !in_quote;
2648                 if (*cur == ',' && !in_quote) {
2649                         gchar *tmp = g_strdup(begin);
2650                         gchar *o_tmp = tmp;
2651                         tmp[cur-begin]='\0';
2652                         cur++;
2653                         begin = cur;
2654                         while (*tmp == ' ' || *tmp == '\t')
2655                                 tmp++;
2656                         compose_add_header_entry(compose, header, tmp, pref_type);
2657                         compose_entry_indicate(compose, tmp);
2658                         g_free(o_tmp);
2659                         continue;
2660                 }
2661                 cur++;
2662         }
2663         if (begin < cur) {
2664                 gchar *tmp = g_strdup(begin);
2665                 gchar *o_tmp = tmp;
2666                 tmp[cur-begin]='\0';
2667                 while (*tmp == ' ' || *tmp == '\t')
2668                         tmp++;
2669                 compose_add_header_entry(compose, header, tmp, pref_type);
2670                 compose_entry_indicate(compose, tmp);
2671                 g_free(o_tmp);          
2672         }
2673 }
2674
2675 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2676 {
2677         GSList *h_list;
2678         GtkEntry *entry;
2679                 
2680         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2681                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2682                 if (gtk_entry_get_text(entry) && 
2683                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2684                                 gtk_widget_modify_base(
2685                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2686                                         GTK_STATE_NORMAL, &default_header_bgcolor);
2687                                 gtk_widget_modify_text(
2688                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2689                                         GTK_STATE_NORMAL, &default_header_color);
2690                 }
2691         }
2692 }
2693
2694 void compose_toolbar_cb(gint action, gpointer data)
2695 {
2696         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2697         Compose *compose = (Compose*)toolbar_item->parent;
2698         
2699         cm_return_if_fail(compose != NULL);
2700
2701         switch(action) {
2702         case A_SEND:
2703                 compose_send_cb(NULL, compose);
2704                 break;
2705         case A_SEND_LATER:
2706                 compose_send_later_cb(NULL, compose);
2707                 break;
2708         case A_DRAFT:
2709                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2710                 break;
2711         case A_INSERT:
2712                 compose_insert_file_cb(NULL, compose);
2713                 break;
2714         case A_ATTACH:
2715                 compose_attach_cb(NULL, compose);
2716                 break;
2717         case A_SIG:
2718                 compose_insert_sig(compose, FALSE);
2719                 break;
2720         case A_REP_SIG:
2721                 compose_insert_sig(compose, TRUE);
2722                 break;
2723         case A_EXTEDITOR:
2724                 compose_ext_editor_cb(NULL, compose);
2725                 break;
2726         case A_LINEWRAP_CURRENT:
2727                 compose_beautify_paragraph(compose, NULL, TRUE);
2728                 break;
2729         case A_LINEWRAP_ALL:
2730                 compose_wrap_all_full(compose, TRUE);
2731                 break;
2732         case A_ADDRBOOK:
2733                 compose_address_cb(NULL, compose);
2734                 break;
2735 #ifdef USE_ENCHANT
2736         case A_CHECK_SPELLING:
2737                 compose_check_all(NULL, compose);
2738                 break;
2739 #endif
2740         default:
2741                 break;
2742         }
2743 }
2744
2745 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2746 {
2747         gchar *to = NULL;
2748         gchar *cc = NULL;
2749         gchar *bcc = NULL;
2750         gchar *subject = NULL;
2751         gchar *body = NULL;
2752         gchar *temp = NULL;
2753         gsize  len = 0;
2754         gchar **attach = NULL;
2755         gchar *inreplyto = NULL;
2756         MailField mfield = NO_FIELD_PRESENT;
2757
2758         /* get mailto parts but skip from */
2759         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2760
2761         if (to) {
2762                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2763                 mfield = TO_FIELD_PRESENT;
2764         }
2765         if (cc)
2766                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2767         if (bcc)
2768                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2769         if (subject) {
2770                 if (!g_utf8_validate (subject, -1, NULL)) {
2771                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2772                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2773                         g_free(temp);
2774                 } else {
2775                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2776                 }
2777                 mfield = SUBJECT_FIELD_PRESENT;
2778         }
2779         if (body) {
2780                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2781                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2782                 GtkTextMark *mark;
2783                 GtkTextIter iter;
2784                 gboolean prev_autowrap = compose->autowrap;
2785
2786                 compose->autowrap = FALSE;
2787
2788                 mark = gtk_text_buffer_get_insert(buffer);
2789                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2790
2791                 if (!g_utf8_validate (body, -1, NULL)) {
2792                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2793                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2794                         g_free(temp);
2795                 } else {
2796                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2797                 }
2798                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2799
2800                 compose->autowrap = prev_autowrap;
2801                 if (compose->autowrap)
2802                         compose_wrap_all(compose);
2803                 mfield = BODY_FIELD_PRESENT;
2804         }
2805
2806         if (attach) {
2807                 gint i = 0, att = 0;
2808                 gchar *warn_files = NULL;
2809                 while (attach[i] != NULL) {
2810                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2811                         if (utf8_filename) {
2812                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2813                                         gchar *tmp = g_strdup_printf("%s%s\n",
2814                                                         warn_files?warn_files:"",
2815                                                         utf8_filename);
2816                                         g_free(warn_files);
2817                                         warn_files = tmp;
2818                                         att++;
2819                                 }
2820                                 g_free(utf8_filename);
2821                         } else {
2822                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2823                         }
2824                         i++;
2825                 }
2826                 if (warn_files) {
2827                         alertpanel_notice(ngettext(
2828                         "The following file has been attached: \n%s",
2829                         "The following files have been attached: \n%s", att), warn_files);
2830                         g_free(warn_files);
2831                 }
2832         }
2833         if (inreplyto)
2834                 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2835
2836         g_free(to);
2837         g_free(cc);
2838         g_free(bcc);
2839         g_free(subject);
2840         g_free(body);
2841         g_strfreev(attach);
2842         g_free(inreplyto);
2843         
2844         return mfield;
2845 }
2846
2847 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2848 {
2849         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2850                                        {"Cc:",          NULL, TRUE},
2851                                        {"References:",  NULL, FALSE},
2852                                        {"Bcc:",         NULL, TRUE},
2853                                        {"Newsgroups:",  NULL, TRUE},
2854                                        {"Followup-To:", NULL, TRUE},
2855                                        {"List-Post:",   NULL, FALSE},
2856                                        {"X-Priority:",  NULL, FALSE},
2857                                        {NULL,           NULL, FALSE}};
2858
2859         enum
2860         {
2861                 H_REPLY_TO      = 0,
2862                 H_CC            = 1,
2863                 H_REFERENCES    = 2,
2864                 H_BCC           = 3,
2865                 H_NEWSGROUPS    = 4,
2866                 H_FOLLOWUP_TO   = 5,
2867                 H_LIST_POST     = 6,
2868                 H_X_PRIORITY    = 7
2869         };
2870
2871         FILE *fp;
2872
2873         cm_return_val_if_fail(msginfo != NULL, -1);
2874
2875         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2876         procheader_get_header_fields(fp, hentry);
2877         fclose(fp);
2878
2879         if (hentry[H_REPLY_TO].body != NULL) {
2880                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2881                         compose->replyto =
2882                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2883                                                    NULL, TRUE);
2884                 }
2885                 g_free(hentry[H_REPLY_TO].body);
2886                 hentry[H_REPLY_TO].body = NULL;
2887         }
2888         if (hentry[H_CC].body != NULL) {
2889                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2890                 g_free(hentry[H_CC].body);
2891                 hentry[H_CC].body = NULL;
2892         }
2893         if (hentry[H_REFERENCES].body != NULL) {
2894                 if (compose->mode == COMPOSE_REEDIT)
2895                         compose->references = hentry[H_REFERENCES].body;
2896                 else {
2897                         compose->references = compose_parse_references
2898                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2899                         g_free(hentry[H_REFERENCES].body);
2900                 }
2901                 hentry[H_REFERENCES].body = NULL;
2902         }
2903         if (hentry[H_BCC].body != NULL) {
2904                 if (compose->mode == COMPOSE_REEDIT)
2905                         compose->bcc =
2906                                 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2907                 g_free(hentry[H_BCC].body);
2908                 hentry[H_BCC].body = NULL;
2909         }
2910         if (hentry[H_NEWSGROUPS].body != NULL) {
2911                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2912                 hentry[H_NEWSGROUPS].body = NULL;
2913         }
2914         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2915                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2916                         compose->followup_to =
2917                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2918                                                    NULL, TRUE);
2919                 }
2920                 g_free(hentry[H_FOLLOWUP_TO].body);
2921                 hentry[H_FOLLOWUP_TO].body = NULL;
2922         }
2923         if (hentry[H_LIST_POST].body != NULL) {
2924                 gchar *to = NULL, *start = NULL;
2925
2926                 extract_address(hentry[H_LIST_POST].body);
2927                 if (hentry[H_LIST_POST].body[0] != '\0') {
2928                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2929                         
2930                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2931                                         NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2932
2933                         if (to) {
2934                                 g_free(compose->ml_post);
2935                                 compose->ml_post = to;
2936                         }
2937                 }
2938                 g_free(hentry[H_LIST_POST].body);
2939                 hentry[H_LIST_POST].body = NULL;
2940         }
2941
2942         /* CLAWS - X-Priority */
2943         if (compose->mode == COMPOSE_REEDIT)
2944                 if (hentry[H_X_PRIORITY].body != NULL) {
2945                         gint priority;
2946                         
2947                         priority = atoi(hentry[H_X_PRIORITY].body);
2948                         g_free(hentry[H_X_PRIORITY].body);
2949                         
2950                         hentry[H_X_PRIORITY].body = NULL;
2951                         
2952                         if (priority < PRIORITY_HIGHEST || 
2953                             priority > PRIORITY_LOWEST)
2954                                 priority = PRIORITY_NORMAL;
2955                         
2956                         compose->priority =  priority;
2957                 }
2958  
2959         if (compose->mode == COMPOSE_REEDIT) {
2960                 if (msginfo->inreplyto && *msginfo->inreplyto)
2961                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2962
2963                 if (msginfo->msgid && *msginfo->msgid)
2964                         compose->msgid = g_strdup(msginfo->msgid);
2965         } else {
2966                 if (msginfo->msgid && *msginfo->msgid)
2967                         compose->inreplyto = g_strdup(msginfo->msgid);
2968
2969                 if (!compose->references) {
2970                         if (msginfo->msgid && *msginfo->msgid) {
2971                                 if (msginfo->inreplyto && *msginfo->inreplyto)
2972                                         compose->references =
2973                                                 g_strdup_printf("<%s>\n\t<%s>",
2974                                                                 msginfo->inreplyto,
2975                                                                 msginfo->msgid);
2976                                 else
2977                                         compose->references =
2978                                                 g_strconcat("<", msginfo->msgid, ">",
2979                                                             NULL);
2980                         } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2981                                 compose->references =
2982                                         g_strconcat("<", msginfo->inreplyto, ">",
2983                                                     NULL);
2984                         }
2985                 }
2986         }
2987
2988         return 0;
2989 }
2990
2991 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2992 {
2993         FILE *fp;
2994         HeaderEntry *he;
2995
2996         cm_return_val_if_fail(msginfo != NULL, -1);
2997
2998         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2999         procheader_get_header_fields(fp, entries);
3000         fclose(fp);
3001
3002         he = entries;
3003         while (he != NULL && he->name != NULL) {
3004                 GtkTreeIter iter;
3005                 GtkListStore *model = NULL;
3006
3007                 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3008                 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3009                 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3010                 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3011                 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3012                 ++he;
3013         }
3014
3015         return 0;
3016 }
3017
3018 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3019 {
3020         GSList *ref_id_list, *cur;
3021         GString *new_ref;
3022         gchar *new_ref_str;
3023
3024         ref_id_list = references_list_append(NULL, ref);
3025         if (!ref_id_list) return NULL;
3026         if (msgid && *msgid)
3027                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3028
3029         for (;;) {
3030                 gint len = 0;
3031
3032                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3033                         /* "<" + Message-ID + ">" + CR+LF+TAB */
3034                         len += strlen((gchar *)cur->data) + 5;
3035
3036                 if (len > MAX_REFERENCES_LEN) {
3037                         /* remove second message-ID */
3038                         if (ref_id_list && ref_id_list->next &&
3039                             ref_id_list->next->next) {
3040                                 g_free(ref_id_list->next->data);
3041                                 ref_id_list = g_slist_remove
3042                                         (ref_id_list, ref_id_list->next->data);
3043                         } else {
3044                                 slist_free_strings_full(ref_id_list);
3045                                 return NULL;
3046                         }
3047                 } else
3048                         break;
3049         }
3050
3051         new_ref = g_string_new("");
3052         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3053                 if (new_ref->len > 0)
3054                         g_string_append(new_ref, "\n\t");
3055                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3056         }
3057
3058         slist_free_strings_full(ref_id_list);
3059
3060         new_ref_str = new_ref->str;
3061         g_string_free(new_ref, FALSE);
3062
3063         return new_ref_str;
3064 }
3065
3066 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3067                                 const gchar *fmt, const gchar *qmark,
3068                                 const gchar *body, gboolean rewrap,
3069                                 gboolean need_unescape,
3070                                 const gchar *err_msg)
3071 {
3072         MsgInfo* dummyinfo = NULL;
3073         gchar *quote_str = NULL;
3074         gchar *buf;
3075         gboolean prev_autowrap;
3076         const gchar *trimmed_body = body;
3077         gint cursor_pos = -1;
3078         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3079         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3080         GtkTextIter iter;
3081         GtkTextMark *mark;
3082         
3083
3084         SIGNAL_BLOCK(buffer);
3085
3086         if (!msginfo) {
3087                 dummyinfo = compose_msginfo_new_from_compose(compose);
3088                 msginfo = dummyinfo;
3089         }
3090
3091         if (qmark != NULL) {
3092 #ifdef USE_ENCHANT
3093                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3094                                 compose->gtkaspell);
3095 #else
3096                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3097 #endif
3098                 quote_fmt_scan_string(qmark);
3099                 quote_fmt_parse();
3100
3101                 buf = quote_fmt_get_buffer();
3102                 if (buf == NULL)
3103                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3104                 else
3105                         Xstrdup_a(quote_str, buf, goto error)
3106         }
3107
3108         if (fmt && *fmt != '\0') {
3109
3110                 if (trimmed_body)
3111                         while (*trimmed_body == '\n')
3112                                 trimmed_body++;
3113
3114 #ifdef USE_ENCHANT
3115                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3116                                 compose->gtkaspell);
3117 #else
3118                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3119 #endif
3120                 if (need_unescape) {
3121                         gchar *tmp = NULL;
3122
3123                         /* decode \-escape sequences in the internal representation of the quote format */
3124                         tmp = g_malloc(strlen(fmt)+1);
3125                         pref_get_unescaped_pref(tmp, fmt);
3126                         quote_fmt_scan_string(tmp);
3127                         quote_fmt_parse();
3128                         g_free(tmp);
3129                 } else {
3130                         quote_fmt_scan_string(fmt);
3131                         quote_fmt_parse();
3132                 }
3133
3134                 buf = quote_fmt_get_buffer();
3135                 if (buf == NULL) {
3136                         gint line = quote_fmt_get_line();
3137                         alertpanel_error(err_msg, line);
3138                         goto error;
3139                 }
3140         } else
3141                 buf = "";
3142
3143         prev_autowrap = compose->autowrap;
3144         compose->autowrap = FALSE;
3145
3146         mark = gtk_text_buffer_get_insert(buffer);
3147         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3148         if (g_utf8_validate(buf, -1, NULL)) { 
3149                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3150         } else {
3151                 gchar *tmpout = NULL;
3152                 tmpout = conv_codeset_strdup
3153                         (buf, conv_get_locale_charset_str_no_utf8(),
3154                          CS_INTERNAL);
3155                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3156                         g_free(tmpout);
3157                         tmpout = g_malloc(strlen(buf)*2+1);
3158                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3159                 }
3160                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3161                 g_free(tmpout);
3162         }
3163
3164         cursor_pos = quote_fmt_get_cursor_pos();
3165         if (cursor_pos == -1)
3166                 cursor_pos = gtk_text_iter_get_offset(&iter);
3167         compose->set_cursor_pos = cursor_pos;
3168
3169         gtk_text_buffer_get_start_iter(buffer, &iter);
3170         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3171         gtk_text_buffer_place_cursor(buffer, &iter);
3172
3173         compose->autowrap = prev_autowrap;
3174         if (compose->autowrap && rewrap)
3175                 compose_wrap_all(compose);