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