Fix crash in mailimf_group_parse() in mailmbox plugin.
[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
111 enum
112 {
113         COL_MIMETYPE = 0,
114         COL_SIZE     = 1,
115         COL_NAME     = 2,
116         COL_CHARSET  = 3,
117         COL_DATA     = 4,
118         COL_AUTODATA = 5,
119         N_COL_COLUMNS
120 };
121
122 #define N_ATTACH_COLS   (N_COL_COLUMNS)
123
124 typedef enum
125 {
126         COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED = -1,
127         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE = 0,
128         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
129         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
130         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
131         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
132         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
133         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
134         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
135         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
136         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
137         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
138         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
139         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
140         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction;
142
143 typedef enum
144 {
145         PRIORITY_HIGHEST = 1,
146         PRIORITY_HIGH,
147         PRIORITY_NORMAL,
148         PRIORITY_LOW,
149         PRIORITY_LOWEST
150 } PriorityLevel;
151
152 typedef enum
153 {
154         COMPOSE_INSERT_SUCCESS,
155         COMPOSE_INSERT_READ_ERROR,
156         COMPOSE_INSERT_INVALID_CHARACTER,
157         COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult;
159
160 typedef enum
161 {
162         COMPOSE_WRITE_FOR_SEND,
163         COMPOSE_WRITE_FOR_STORE
164 } ComposeWriteType;
165
166 typedef enum
167 {
168         COMPOSE_QUOTE_FORCED,
169         COMPOSE_QUOTE_CHECK,
170         COMPOSE_QUOTE_SKIP
171 } ComposeQuoteMode;
172
173 typedef enum {
174     TO_FIELD_PRESENT,
175     SUBJECT_FIELD_PRESENT,
176     BODY_FIELD_PRESENT,
177     NO_FIELD_PRESENT
178 } MailField;
179
180 #define B64_LINE_SIZE           57
181 #define B64_BUFFSIZE            77
182
183 #define MAX_REFERENCES_LEN      999
184
185 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
186 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
187
188 static GdkColor default_header_bgcolor = {
189         (gulong)0,
190         (gushort)0,
191         (gushort)0,
192         (gushort)0
193 };
194
195 static GdkColor default_header_color = {
196         (gulong)0,
197         (gushort)0,
198         (gushort)0,
199         (gushort)0
200 };
201
202 static GList *compose_list = NULL;
203 static GSList *extra_headers = NULL;
204
205 static Compose *compose_generic_new                     (PrefsAccount   *account,
206                                                  const gchar    *to,
207                                                  FolderItem     *item,
208                                                  GList          *attach_files,
209                                                  GList          *listAddress );
210
211 static Compose *compose_create                  (PrefsAccount   *account,
212                                                  FolderItem              *item,
213                                                  ComposeMode     mode,
214                                                  gboolean batch);
215
216 static void compose_entry_indicate      (Compose          *compose,
217                                          const gchar      *address);
218 static Compose *compose_followup_and_reply_to   (MsgInfo        *msginfo,
219                                          ComposeQuoteMode        quote_mode,
220                                          gboolean        to_all,
221                                          gboolean        to_sender,
222                                          const gchar    *body);
223 static Compose *compose_forward_multiple        (PrefsAccount   *account, 
224                                          GSList         *msginfo_list);
225 static Compose *compose_reply                   (MsgInfo        *msginfo,
226                                          ComposeQuoteMode        quote_mode,
227                                          gboolean        to_all,
228                                          gboolean        to_ml,
229                                          gboolean        to_sender,
230                                          const gchar    *body);
231 static Compose *compose_reply_mode              (ComposeMode     mode, 
232                                          GSList         *msginfo_list, 
233                                          gchar          *body);
234 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
235 static void compose_update_privacy_systems_menu(Compose *compose);
236
237 static GtkWidget *compose_account_option_menu_create
238                                                 (Compose        *compose);
239 static void compose_set_out_encoding            (Compose        *compose);
240 static void compose_set_template_menu           (Compose        *compose);
241 static void compose_destroy                     (Compose        *compose);
242
243 static MailField compose_entries_set            (Compose        *compose,
244                                                  const gchar    *mailto,
245                                                  ComposeEntryType to_type);
246 static gint compose_parse_header                (Compose        *compose,
247                                                  MsgInfo        *msginfo);
248 static gint compose_parse_manual_headers        (Compose        *compose,
249                                                  MsgInfo        *msginfo,
250                                                  HeaderEntry    *entries);
251 static gchar *compose_parse_references          (const gchar    *ref,
252                                                  const gchar    *msgid);
253
254 static gchar *compose_quote_fmt                 (Compose        *compose,
255                                                  MsgInfo        *msginfo,
256                                                  const gchar    *fmt,
257                                                  const gchar    *qmark,
258                                                  const gchar    *body,
259                                                  gboolean        rewrap,
260                                                  gboolean        need_unescape,
261                                                  const gchar *err_msg);
262
263 static void compose_reply_set_entry             (Compose        *compose,
264                                                  MsgInfo        *msginfo,
265                                                  gboolean        to_all,
266                                                  gboolean        to_ml,
267                                                  gboolean        to_sender,
268                                                  gboolean
269                                                  followup_and_reply_to);
270 static void compose_reedit_set_entry            (Compose        *compose,
271                                                  MsgInfo        *msginfo);
272
273 static void compose_insert_sig                  (Compose        *compose,
274                                                  gboolean        replace);
275 static ComposeInsertResult compose_insert_file  (Compose        *compose,
276                                                  const gchar    *file);
277
278 static gboolean compose_attach_append           (Compose        *compose,
279                                                  const gchar    *file,
280                                                  const gchar    *type,
281                                                  const gchar    *content_type,
282                                                  const gchar    *charset);
283 static void compose_attach_parts                (Compose        *compose,
284                                                  MsgInfo        *msginfo);
285
286 static gboolean compose_beautify_paragraph      (Compose        *compose,
287                                                  GtkTextIter    *par_iter,
288                                                  gboolean        force);
289 static void compose_wrap_all                    (Compose        *compose);
290 static void compose_wrap_all_full               (Compose        *compose,
291                                                  gboolean        autowrap);
292
293 static void compose_set_title                   (Compose        *compose);
294 static void compose_select_account              (Compose        *compose,
295                                                  PrefsAccount   *account,
296                                                  gboolean        init);
297
298 static PrefsAccount *compose_current_mail_account(void);
299 /* static gint compose_send                     (Compose        *compose); */
300 static gboolean compose_check_for_valid_recipient
301                                                 (Compose        *compose);
302 static gboolean compose_check_entries           (Compose        *compose,
303                                                  gboolean       check_everything);
304 static gint compose_write_to_file               (Compose        *compose,
305                                                  FILE           *fp,
306                                                  gint            action,
307                                                  gboolean        attach_parts);
308 static gint compose_write_body_to_file          (Compose        *compose,
309                                                  const gchar    *file);
310 static gint compose_remove_reedit_target        (Compose        *compose,
311                                                  gboolean        force);
312 static void compose_remove_draft                        (Compose        *compose);
313 static gint compose_queue_sub                   (Compose        *compose,
314                                                  gint           *msgnum,
315                                                  FolderItem     **item,
316                                                  gchar          **msgpath,
317                                                  gboolean       perform_checks,
318                                                  gboolean       remove_reedit_target);
319 static int compose_add_attachments              (Compose        *compose,
320                                                  MimeInfo       *parent);
321 static gchar *compose_get_header                (Compose        *compose);
322 static gchar *compose_get_manual_headers_info   (Compose        *compose);
323
324 static void compose_convert_header              (Compose        *compose,
325                                                  gchar          *dest,
326                                                  gint            len,
327                                                  gchar          *src,
328                                                  gint            header_len,
329                                                  gboolean        addr_field);
330
331 static void compose_attach_info_free            (AttachInfo     *ainfo);
332 static void compose_attach_remove_selected      (GtkAction      *action,
333                                                  gpointer        data);
334
335 static void compose_template_apply              (Compose        *compose,
336                                                  Template       *tmpl,
337                                                  gboolean        replace);
338 static void compose_attach_property             (GtkAction      *action,
339                                                  gpointer        data);
340 static void compose_attach_property_create      (gboolean       *cancelled);
341 static void attach_property_ok                  (GtkWidget      *widget,
342                                                  gboolean       *cancelled);
343 static void attach_property_cancel              (GtkWidget      *widget,
344                                                  gboolean       *cancelled);
345 static gint attach_property_delete_event        (GtkWidget      *widget,
346                                                  GdkEventAny    *event,
347                                                  gboolean       *cancelled);
348 static gboolean attach_property_key_pressed     (GtkWidget      *widget,
349                                                  GdkEventKey    *event,
350                                                  gboolean       *cancelled);
351
352 static void compose_exec_ext_editor             (Compose        *compose);
353 #ifdef G_OS_UNIX
354 static gint compose_exec_ext_editor_real        (const gchar    *file,
355                                                  GdkNativeWindow socket_wid);
356 static gboolean compose_ext_editor_kill         (Compose        *compose);
357 static gboolean compose_input_cb                (GIOChannel     *source,
358                                                  GIOCondition    condition,
359                                                  gpointer        data);
360 static void compose_set_ext_editor_sensitive    (Compose        *compose,
361                                                  gboolean        sensitive);
362 static gboolean compose_get_ext_editor_cmd_valid();
363 static gboolean compose_get_ext_editor_uses_socket();
364 static gboolean compose_ext_editor_plug_removed_cb
365                                                 (GtkSocket      *socket,
366                                                  Compose        *compose);
367 #endif /* G_OS_UNIX */
368
369 static void compose_undo_state_changed          (UndoMain       *undostruct,
370                                                  gint            undo_state,
371                                                  gint            redo_state,
372                                                  gpointer        data);
373
374 static void compose_create_header_entry (Compose *compose);
375 static void compose_add_header_entry    (Compose *compose, const gchar *header,
376                                          gchar *text, ComposePrefType pref_type);
377 static void compose_remove_header_entries(Compose *compose);
378
379 static void compose_update_priority_menu_item(Compose * compose);
380 #if USE_ENCHANT
381 static void compose_spell_menu_changed  (void *data);
382 static void compose_dict_changed        (void *data);
383 #endif
384 static void compose_add_field_list      ( Compose *compose,
385                                           GList *listAddress );
386
387 /* callback functions */
388
389 static void compose_notebook_size_alloc (GtkNotebook *notebook,
390                                          GtkAllocation *allocation,
391                                          GtkPaned *paned);
392 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
393                                          GtkAllocation  *allocation,
394                                          GtkSHRuler     *shruler);
395 static void account_activated           (GtkComboBox *optmenu,
396                                          gpointer        data);
397 static void attach_selected             (GtkTreeView    *tree_view, 
398                                          GtkTreePath    *tree_path,
399                                          GtkTreeViewColumn *column, 
400                                          Compose *compose);
401 static gboolean attach_button_pressed   (GtkWidget      *widget,
402                                          GdkEventButton *event,
403                                          gpointer        data);
404 static gboolean attach_key_pressed      (GtkWidget      *widget,
405                                          GdkEventKey    *event,
406                                          gpointer        data);
407 static void compose_send_cb             (GtkAction      *action, gpointer data);
408 static void compose_send_later_cb       (GtkAction      *action, gpointer data);
409
410 static void compose_save_cb             (GtkAction      *action,
411                                          gpointer        data);
412
413 static void compose_attach_cb           (GtkAction      *action,
414                                          gpointer        data);
415 static void compose_insert_file_cb      (GtkAction      *action,
416                                          gpointer        data);
417 static void compose_insert_sig_cb       (GtkAction      *action,
418                                          gpointer        data);
419 static void compose_replace_sig_cb      (GtkAction      *action,
420                                          gpointer        data);
421
422 static void compose_close_cb            (GtkAction      *action,
423                                          gpointer        data);
424 static void compose_print_cb            (GtkAction      *action,
425                                          gpointer        data);
426
427 static void compose_set_encoding_cb     (GtkAction      *action, GtkRadioAction *current, gpointer data);
428
429 static void compose_address_cb          (GtkAction      *action,
430                                          gpointer        data);
431 static void about_show_cb               (GtkAction      *action,
432                                          gpointer        data);
433 static void compose_template_activate_cb(GtkWidget      *widget,
434                                          gpointer        data);
435
436 static void compose_ext_editor_cb       (GtkAction      *action,
437                                          gpointer        data);
438
439 static gint compose_delete_cb           (GtkWidget      *widget,
440                                          GdkEventAny    *event,
441                                          gpointer        data);
442
443 static void compose_undo_cb             (GtkAction      *action,
444                                          gpointer        data);
445 static void compose_redo_cb             (GtkAction      *action,
446                                          gpointer        data);
447 static void compose_cut_cb              (GtkAction      *action,
448                                          gpointer        data);
449 static void compose_copy_cb             (GtkAction      *action,
450                                          gpointer        data);
451 static void compose_paste_cb            (GtkAction      *action,
452                                          gpointer        data);
453 static void compose_paste_as_quote_cb   (GtkAction      *action,
454                                          gpointer        data);
455 static void compose_paste_no_wrap_cb    (GtkAction      *action,
456                                          gpointer        data);
457 static void compose_paste_wrap_cb       (GtkAction      *action,
458                                          gpointer        data);
459 static void compose_allsel_cb           (GtkAction      *action,
460                                          gpointer        data);
461
462 static void compose_advanced_action_cb  (GtkAction      *action,
463                                          gpointer        data);
464
465 static void compose_grab_focus_cb       (GtkWidget      *widget,
466                                          Compose        *compose);
467
468 static void compose_changed_cb          (GtkTextBuffer  *textbuf,
469                                          Compose        *compose);
470
471 static void compose_wrap_cb             (GtkAction      *action,
472                                          gpointer        data);
473 static void compose_wrap_all_cb         (GtkAction      *action,
474                                          gpointer        data);
475 static void compose_find_cb             (GtkAction      *action,
476                                          gpointer        data);
477 static void compose_toggle_autowrap_cb  (GtkToggleAction *action,
478                                          gpointer        data);
479 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
480                                          gpointer        data);
481
482 static void compose_toggle_ruler_cb     (GtkToggleAction *action,
483                                          gpointer        data);
484 static void compose_toggle_sign_cb      (GtkToggleAction *action,
485                                          gpointer        data);
486 static void compose_toggle_encrypt_cb   (GtkToggleAction *action,
487                                          gpointer        data);
488 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
489 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
490 static void activate_privacy_system     (Compose *compose, 
491                                          PrefsAccount *account,
492                                          gboolean warn);
493 static void compose_use_signing(Compose *compose, gboolean use_signing);
494 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
495 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
496                                          gpointer        data);
497 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
498                                          gpointer        data);
499 static void compose_set_priority_cb     (GtkAction *action, GtkRadioAction *current, gpointer data);
500 static void compose_reply_change_mode   (Compose *compose, ComposeMode action);
501 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
502
503 static void compose_attach_drag_received_cb (GtkWidget          *widget,
504                                              GdkDragContext     *drag_context,
505                                              gint                x,
506                                              gint                y,
507                                              GtkSelectionData   *data,
508                                              guint               info,
509                                              guint               time,
510                                              gpointer            user_data);
511 static void compose_insert_drag_received_cb (GtkWidget          *widget,
512                                              GdkDragContext     *drag_context,
513                                              gint                x,
514                                              gint                y,
515                                              GtkSelectionData   *data,
516                                              guint               info,
517                                              guint               time,
518                                              gpointer            user_data);
519 static void compose_header_drag_received_cb (GtkWidget          *widget,
520                                              GdkDragContext     *drag_context,
521                                              gint                x,
522                                              gint                y,
523                                              GtkSelectionData   *data,
524                                              guint               info,
525                                              guint               time,
526                                              gpointer            user_data);
527
528 static gboolean compose_drag_drop           (GtkWidget *widget,
529                                              GdkDragContext *drag_context,
530                                              gint x, gint y,
531                                              guint time, gpointer user_data);
532 static gboolean completion_set_focus_to_subject
533                                         (GtkWidget    *widget,
534                                          GdkEventKey  *event,
535                                          Compose      *user_data);
536
537 static void text_inserted               (GtkTextBuffer  *buffer,
538                                          GtkTextIter    *iter,
539                                          const gchar    *text,
540                                          gint            len,
541                                          Compose        *compose);
542 static Compose *compose_generic_reply(MsgInfo *msginfo,
543                                   ComposeQuoteMode quote_mode,
544                                   gboolean to_all,
545                                   gboolean to_ml,
546                                   gboolean to_sender,
547                                   gboolean followup_and_reply_to,
548                                   const gchar *body);
549
550 static void compose_headerentry_changed_cb         (GtkWidget          *entry,
551                                             ComposeHeaderEntry *headerentry);
552 static gboolean compose_headerentry_key_press_event_cb(GtkWidget               *entry,
553                                             GdkEventKey        *event,
554                                             ComposeHeaderEntry *headerentry);
555 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
556                                         ComposeHeaderEntry *headerentry);
557
558 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
559
560 static void compose_allow_user_actions (Compose *compose, gboolean allow);
561
562 static void compose_nothing_cb             (GtkAction *action, gpointer data)
563 {
564
565 }
566
567 #if USE_ENCHANT
568 static void compose_check_all              (GtkAction *action, gpointer data);
569 static void compose_highlight_all          (GtkAction *action, gpointer data);
570 static void compose_check_backwards        (GtkAction *action, gpointer data);
571 static void compose_check_forwards_go      (GtkAction *action, gpointer data);
572 #endif
573
574 static PrefsAccount *compose_find_account       (MsgInfo *msginfo);
575
576 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
577
578 #ifdef USE_ENCHANT
579 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
580                                                 FolderItem *folder_item);
581 #endif
582 static void compose_attach_update_label(Compose *compose);
583 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
584                                      gboolean respect_default_to);
585 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
586 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
587
588 static GtkActionEntry compose_popup_entries[] =
589 {
590         {"Compose",            NULL, "Compose", NULL, NULL, NULL },
591         {"Compose/Add",        NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
592         {"Compose/Remove",     NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
593         {"Compose/---",        NULL, "---", NULL, NULL, NULL },
594         {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
595 };
596
597 static GtkActionEntry compose_entries[] =
598 {
599         {"Menu",                          NULL, "Menu", NULL, NULL, NULL },
600 /* menus */
601         {"Message",                       NULL, N_("_Message"), NULL, NULL, NULL },
602         {"Edit",                          NULL, N_("_Edit"), NULL, NULL, NULL },
603 #if USE_ENCHANT
604         {"Spelling",                      NULL, N_("_Spelling"), NULL, NULL, NULL },
605 #endif
606         {"Options",                       NULL, N_("_Options"), NULL, NULL, NULL },
607         {"Tools",                         NULL, N_("_Tools"), NULL, NULL, NULL },
608         {"Help",                          NULL, N_("_Help"), NULL, NULL, NULL },
609 /* Message menu */
610         {"Message/Send",                  NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
611         {"Message/SendLater",             NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
612         {"Message/---",                   NULL, "---", NULL, NULL, NULL },
613
614         {"Message/AttachFile",            NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
615         {"Message/InsertFile",            NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
616         {"Message/InsertSig",             NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
617         {"Message/ReplaceSig",            NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
618         /* {"Message/---",                NULL, "---", NULL, NULL, NULL }, */
619         {"Message/Save",                  NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
620         /* {"Message/---",                NULL, "---", NULL, NULL, NULL }, */
621         {"Message/Print",                 NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
622         /* {"Message/---",                NULL, "---", NULL, NULL, NULL }, */
623         {"Message/Close",                 NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
624
625 /* Edit menu */
626         {"Edit/Undo",                     NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
627         {"Edit/Redo",                     NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
628         {"Edit/---",                      NULL, "---", NULL, NULL, NULL },
629
630         {"Edit/Cut",                      NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
631         {"Edit/Copy",                     NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
632         {"Edit/Paste",                    NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
633
634         {"Edit/SpecialPaste",             NULL, N_("_Special paste"), NULL, NULL, NULL },
635         {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
636         {"Edit/SpecialPaste/Wrapped",     NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
637         {"Edit/SpecialPaste/Unwrapped",   NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
638
639         {"Edit/SelectAll",                NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
640
641         {"Edit/Advanced",                 NULL, N_("A_dvanced"), NULL, NULL, NULL },
642         {"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*/
643         {"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*/
644         {"Edit/Advanced/BackWord",        NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
645         {"Edit/Advanced/ForwWord",        NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
646         {"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*/
647         {"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*/
648         {"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*/
649         {"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*/
650         {"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*/
651         {"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*/
652         {"Edit/Advanced/DelBackWord",     NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
653         {"Edit/Advanced/DelForwWord",     NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
654         {"Edit/Advanced/DelLine",         NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
655         {"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*/
656
657         /* {"Edit/---",                   NULL, "---", NULL, NULL, NULL }, */
658         {"Edit/Find",                     NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
659
660         /* {"Edit/---",                   NULL, "---", NULL, NULL, NULL }, */
661         {"Edit/WrapPara",                 NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
662         {"Edit/WrapAllLines",             NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
663         /* {"Edit/---",                   NULL, "---", NULL, NULL, NULL }, */
664         {"Edit/ExtEditor",                NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
665 #if USE_ENCHANT
666 /* Spelling menu */
667         {"Spelling/CheckAllSel",          NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
668         {"Spelling/HighlightAll",         NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
669         {"Spelling/CheckBackwards",       NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
670         {"Spelling/ForwardNext",          NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
671
672         {"Spelling/---",                  NULL, "---", NULL, NULL, NULL },
673         {"Spelling/Options",              NULL, N_("_Options"), NULL, NULL, NULL },
674 #endif
675
676 /* Options menu */
677         {"Options/ReplyMode",                 NULL, N_("Reply _mode"), NULL, NULL, NULL },
678         {"Options/---",                       NULL, "---", NULL, NULL, NULL },
679         {"Options/PrivacySystem",             NULL, N_("Privacy _System"), NULL, NULL, NULL },
680         {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
681
682         /* {"Options/---",                NULL, "---", NULL, NULL, NULL }, */
683         {"Options/Priority",              NULL, N_("_Priority"), NULL, NULL, NULL },
684
685         {"Options/Encoding",              NULL, N_("Character _encoding"), NULL, NULL, NULL },
686         {"Options/Encoding/---",          NULL, "---", NULL, NULL, NULL },
687 #define ENC_ACTION(cs_char,c_char,string) \
688         {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
689
690         {"Options/Encoding/Western",      NULL, N_("Western European"), NULL, NULL, NULL },
691         {"Options/Encoding/Baltic",       NULL, N_("Baltic"), NULL, NULL, NULL },
692         {"Options/Encoding/Hebrew",       NULL, N_("Hebrew"), NULL, NULL, NULL },
693         {"Options/Encoding/Arabic",       NULL, N_("Arabic"), NULL, NULL, NULL },
694         {"Options/Encoding/Cyrillic",     NULL, N_("Cyrillic"), NULL, NULL, NULL },
695         {"Options/Encoding/Japanese",     NULL, N_("Japanese"), NULL, NULL, NULL },
696         {"Options/Encoding/Chinese",      NULL, N_("Chinese"), NULL, NULL, NULL },
697         {"Options/Encoding/Korean",       NULL, N_("Korean"), NULL, NULL, NULL },
698         {"Options/Encoding/Thai",         NULL, N_("Thai"), NULL, NULL, NULL },
699
700 /* Tools menu */
701         {"Tools/AddressBook",             NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) }, 
702
703         {"Tools/Template",                NULL, N_("_Template"), NULL, NULL, NULL },
704         {"Tools/Template/PlaceHolder",    NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
705         {"Tools/Actions",                 NULL, N_("Actio_ns"), NULL, NULL, NULL },
706         {"Tools/Actions/PlaceHolder",     NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
707
708 /* Help menu */
709         {"Help/About",                    NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) }, 
710 };
711
712 static GtkToggleActionEntry compose_toggle_entries[] =
713 {
714         {"Edit/AutoWrap",            NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb), FALSE }, /* Toggle */
715         {"Edit/AutoIndent",          NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb), FALSE }, /* Toggle */
716         {"Options/Sign",             NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb), FALSE }, /* Toggle */
717         {"Options/Encrypt",          NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb), FALSE }, /* Toggle */
718         {"Options/RequestRetRcpt",   NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb), FALSE }, /* Toggle */
719         {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb), FALSE }, /* Toggle */
720         {"Tools/ShowRuler",          NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb), FALSE }, /* Toggle */
721 };
722
723 static GtkRadioActionEntry compose_radio_rm_entries[] =
724 {
725         {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
726         {"Options/ReplyMode/All",    NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
727         {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
728         {"Options/ReplyMode/List",   NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
729 };
730
731 static GtkRadioActionEntry compose_radio_prio_entries[] =
732 {
733         {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
734         {"Options/Priority/High",    NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
735         {"Options/Priority/Normal",  NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
736         {"Options/Priority/Low",     NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
737         {"Options/Priority/Lowest",  NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
738 };
739
740 static GtkRadioActionEntry compose_radio_enc_entries[] =
741 {
742         ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
743         ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
744         ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
745         ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
746         ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
747         ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
748         ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
749         ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
750         ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
751         ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
752         ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
753         ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
754         ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
755         ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
756         ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
757         ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
758         ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
759         ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
760         ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
761         ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
762         ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
763         ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
764         ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
765         ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
766         ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
767         ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
768         ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
769         ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
770         ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
771         ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
772         ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
773         ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
774         ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
775 };
776
777 static GtkTargetEntry compose_mime_types[] =
778 {
779         {"text/uri-list", 0, 0},
780         {"UTF8_STRING", 0, 0},
781         {"text/plain", 0, 0}
782 };
783
784 static gboolean compose_put_existing_to_front(MsgInfo *info)
785 {
786         const GList *compose_list = compose_get_compose_list();
787         const GList *elem = NULL;
788         
789         if (compose_list) {
790                 for (elem = compose_list; elem != NULL && elem->data != NULL; 
791                      elem = elem->next) {
792                         Compose *c = (Compose*)elem->data;
793
794                         if (!c->targetinfo || !c->targetinfo->msgid ||
795                             !info->msgid)
796                                 continue;
797
798                         if (!strcmp(c->targetinfo->msgid, info->msgid)) {
799                                 gtkut_window_popup(c->window);
800                                 return TRUE;
801                         }
802                 }
803         }
804         return FALSE;
805 }
806
807 static GdkColor quote_color1 = 
808         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
809 static GdkColor quote_color2 = 
810         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
811 static GdkColor quote_color3 = 
812         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
813
814 static GdkColor quote_bgcolor1 = 
815         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
816 static GdkColor quote_bgcolor2 = 
817         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
818 static GdkColor quote_bgcolor3 = 
819         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
820
821 static GdkColor signature_color = {
822         (gulong)0,
823         (gushort)0x7fff,
824         (gushort)0x7fff,
825         (gushort)0x7fff
826 };
827
828 static GdkColor uri_color = {
829         (gulong)0,
830         (gushort)0,
831         (gushort)0,
832         (gushort)0
833 };
834
835 static void compose_create_tags(GtkTextView *text, Compose *compose)
836 {
837         GtkTextBuffer *buffer;
838         GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
839 #if !GTK_CHECK_VERSION(2, 24, 0)
840         GdkColormap *cmap;
841         gboolean success[8];
842         int i;
843         GdkColor color[8];
844 #endif
845
846         buffer = gtk_text_view_get_buffer(text);
847
848         if (prefs_common.enable_color) {
849                 /* grab the quote colors, converting from an int to a GdkColor */
850                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
851                                                &quote_color1);
852                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
853                                                &quote_color2);
854                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
855                                                &quote_color3);
856                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
857                                                &quote_bgcolor1);
858                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
859                                                &quote_bgcolor2);
860                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
861                                                &quote_bgcolor3);
862                 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
863                                                &signature_color);
864                 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
865                                                &uri_color);
866         } else {
867                 signature_color = quote_color1 = quote_color2 = quote_color3 = 
868                         quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
869         }
870
871         if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
872                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
873                                            "foreground-gdk", &quote_color1,
874                                            "paragraph-background-gdk", &quote_bgcolor1,
875                                            NULL);
876                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
877                                            "foreground-gdk", &quote_color2,
878                                            "paragraph-background-gdk", &quote_bgcolor2,
879                                            NULL);
880                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
881                                            "foreground-gdk", &quote_color3,
882                                            "paragraph-background-gdk", &quote_bgcolor3,
883                                            NULL);
884         } else {
885                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
886                                            "foreground-gdk", &quote_color1,
887                                            NULL);
888                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
889                                            "foreground-gdk", &quote_color2,
890                                            NULL);
891                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
892                                            "foreground-gdk", &quote_color3,
893                                            NULL);
894         }
895         
896         compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
897                                    "foreground-gdk", &signature_color,
898                                    NULL);
899         
900         compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
901                                         "foreground-gdk", &uri_color,
902                                          NULL);
903         compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
904         compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
905
906 #if !GTK_CHECK_VERSION(2, 24, 0)
907         color[0] = quote_color1;
908         color[1] = quote_color2;
909         color[2] = quote_color3;
910         color[3] = quote_bgcolor1;
911         color[4] = quote_bgcolor2;
912         color[5] = quote_bgcolor3;
913         color[6] = signature_color;
914         color[7] = uri_color;
915
916         cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
917         gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
918
919         for (i = 0; i < 8; i++) {
920                 if (success[i] == FALSE) {
921                         g_warning("Compose: color allocation failed.");
922                         quote_color1 = quote_color2 = quote_color3 = 
923                                 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = 
924                                 signature_color = uri_color = black;
925                 }
926         }
927 #endif
928 }
929
930 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
931                      GList *attach_files)
932 {
933         return compose_generic_new(account, mailto, NULL, attach_files, NULL);
934 }
935
936 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
937 {
938         return compose_generic_new(account, mailto, item, NULL, NULL);
939 }
940
941 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
942 {
943         return compose_generic_new( account, NULL, NULL, NULL, listAddress );
944 }
945
946 #define SCROLL_TO_CURSOR(compose) {                             \
947         GtkTextMark *cmark = gtk_text_buffer_get_insert(        \
948                 gtk_text_view_get_buffer(                       \
949                         GTK_TEXT_VIEW(compose->text)));         \
950         gtk_text_view_scroll_mark_onscreen(                     \
951                 GTK_TEXT_VIEW(compose->text),                   \
952                 cmark);                                         \
953 }
954
955 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
956 {
957         GtkEditable *entry;
958         if (folderidentifier) {
959 #if !GTK_CHECK_VERSION(2, 24, 0)
960                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
961 #else
962                 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
963 #endif
964                 prefs_common.compose_save_to_history = add_history(
965                                 prefs_common.compose_save_to_history, folderidentifier);
966 #if !GTK_CHECK_VERSION(2, 24, 0)
967                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
968                                 prefs_common.compose_save_to_history);
969 #else
970                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
971                                 prefs_common.compose_save_to_history);
972 #endif
973         }
974
975         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
976         if (folderidentifier)
977                 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
978         else
979                 gtk_entry_set_text(GTK_ENTRY(entry), "");
980 }
981
982 static gchar *compose_get_save_to(Compose *compose)
983 {
984         GtkEditable *entry;
985         gchar *result = NULL;
986         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
987         result = gtk_editable_get_chars(entry, 0, -1);
988         
989         if (result) {
990 #if !GTK_CHECK_VERSION(2, 24, 0)
991                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
992 #else
993                 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
994 #endif
995                 prefs_common.compose_save_to_history = add_history(
996                                 prefs_common.compose_save_to_history, result);
997 #if !GTK_CHECK_VERSION(2, 24, 0)
998                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
999                                 prefs_common.compose_save_to_history);
1000 #else
1001                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
1002                                 prefs_common.compose_save_to_history);
1003 #endif
1004         }
1005         return result;
1006 }
1007
1008 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
1009                              GList *attach_files, GList *listAddress )
1010 {
1011         Compose *compose;
1012         GtkTextView *textview;
1013         GtkTextBuffer *textbuf;
1014         GtkTextIter iter;
1015         const gchar *subject_format = NULL;
1016         const gchar *body_format = NULL;
1017         gchar *mailto_from = NULL;
1018         PrefsAccount *mailto_account = NULL;
1019         MsgInfo* dummyinfo = NULL;
1020         gint cursor_pos = -1;
1021         MailField mfield = NO_FIELD_PRESENT;
1022         gchar* buf;
1023         GtkTextMark *mark;
1024
1025         /* check if mailto defines a from */
1026         if (mailto && *mailto != '\0') {
1027                 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1028                 /* mailto defines a from, check if we can get account prefs from it,
1029                    if not, the account prefs will be guessed using other ways, but we'll keep
1030                    the from anyway */
1031                 if (mailto_from) {
1032                         mailto_account = account_find_from_address(mailto_from, TRUE);
1033                         if (mailto_account == NULL) {
1034                                 gchar *tmp_from;
1035                                 Xstrdup_a(tmp_from, mailto_from, return NULL);
1036                                 extract_address(tmp_from);
1037                                 mailto_account = account_find_from_address(tmp_from, TRUE);
1038                         }
1039                 }
1040                 if (mailto_account)
1041                         account = mailto_account;
1042         }
1043
1044         /* if no account prefs set from mailto, set if from folder prefs (if any) */
1045         if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1046                 account = account_find_from_id(item->prefs->default_account);
1047
1048         /* if no account prefs set, fallback to the current one */
1049         if (!account) account = cur_account;
1050         cm_return_val_if_fail(account != NULL, NULL);
1051
1052         compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1053
1054         /* override from name if mailto asked for it */
1055         if (mailto_from) {
1056                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1057                 g_free(mailto_from);
1058         } else
1059                 /* override from name according to folder properties */
1060                 if (item && item->prefs &&
1061                         item->prefs->compose_with_format &&
1062                         item->prefs->compose_override_from_format &&
1063                         *item->prefs->compose_override_from_format != '\0') {
1064
1065                         gchar *tmp = NULL;
1066                         gchar *buf = NULL;
1067
1068                         dummyinfo = compose_msginfo_new_from_compose(compose);
1069
1070                         /* decode \-escape sequences in the internal representation of the quote format */
1071                         tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1072                         pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1073
1074 #ifdef USE_ENCHANT
1075                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1076                                         compose->gtkaspell);
1077 #else
1078                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1079 #endif
1080                         quote_fmt_scan_string(tmp);
1081                         quote_fmt_parse();
1082
1083                         buf = quote_fmt_get_buffer();
1084                         if (buf == NULL)
1085                                 alertpanel_error(_("New message From format error."));
1086                         else
1087                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1088                         quote_fmt_reset_vartable();
1089
1090                         g_free(tmp);
1091                 }
1092
1093         compose->replyinfo = NULL;
1094         compose->fwdinfo   = NULL;
1095
1096         textview = GTK_TEXT_VIEW(compose->text);
1097         textbuf = gtk_text_view_get_buffer(textview);
1098         compose_create_tags(textview, compose);
1099
1100         undo_block(compose->undostruct);
1101 #ifdef USE_ENCHANT
1102         compose_set_dictionaries_from_folder_prefs(compose, item);
1103 #endif
1104
1105         if (account->auto_sig)
1106                 compose_insert_sig(compose, FALSE);
1107         gtk_text_buffer_get_start_iter(textbuf, &iter);
1108         gtk_text_buffer_place_cursor(textbuf, &iter);
1109
1110         if (account->protocol != A_NNTP) {
1111                 if (mailto && *mailto != '\0') {
1112                         mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1113
1114                 } else {
1115                         compose_set_folder_prefs(compose, item, TRUE);
1116                 }
1117                 if (item && item->ret_rcpt) {
1118                         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1119                 }
1120         } else {
1121                 if (mailto && *mailto != '\0') {
1122                         if (!strchr(mailto, '@'))
1123                                 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1124                         else
1125                                 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1126                 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1127                         compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1128                         mfield = TO_FIELD_PRESENT;
1129                 }
1130                 /*
1131                  * CLAWS: just don't allow return receipt request, even if the user
1132                  * may want to send an email. simple but foolproof.
1133                  */
1134                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE); 
1135         }
1136         compose_add_field_list( compose, listAddress );
1137
1138         if (item && item->prefs && item->prefs->compose_with_format) {
1139                 subject_format = item->prefs->compose_subject_format;
1140                 body_format = item->prefs->compose_body_format;
1141         } else if (account->compose_with_format) {
1142                 subject_format = account->compose_subject_format;
1143                 body_format = account->compose_body_format;
1144         } else if (prefs_common.compose_with_format) {
1145                 subject_format = prefs_common.compose_subject_format;
1146                 body_format = prefs_common.compose_body_format;
1147         }
1148
1149         if (subject_format || body_format) {
1150
1151                 if ( subject_format
1152                          && *subject_format != '\0' )
1153                 {
1154                         gchar *subject = NULL;
1155                         gchar *tmp = NULL;
1156                         gchar *buf = NULL;
1157
1158                         if (!dummyinfo)
1159                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1160
1161                         /* decode \-escape sequences in the internal representation of the quote format */
1162                         tmp = g_malloc(strlen(subject_format)+1);
1163                         pref_get_unescaped_pref(tmp, subject_format);
1164
1165                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1166 #ifdef USE_ENCHANT
1167                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1168                                         compose->gtkaspell);
1169 #else
1170                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1171 #endif
1172                         quote_fmt_scan_string(tmp);
1173                         quote_fmt_parse();
1174
1175                         buf = quote_fmt_get_buffer();
1176                         if (buf == NULL)
1177                                 alertpanel_error(_("New message subject format error."));
1178                         else
1179                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1180                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1181                         quote_fmt_reset_vartable();
1182
1183                         g_free(subject);
1184                         g_free(tmp);
1185                         mfield = SUBJECT_FIELD_PRESENT;
1186                 }
1187
1188                 if ( body_format
1189                          && *body_format != '\0' )
1190                 {
1191                         GtkTextView *text;
1192                         GtkTextBuffer *buffer;
1193                         GtkTextIter start, end;
1194                         gchar *tmp = NULL;
1195
1196                         if (!dummyinfo)
1197                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1198
1199                         text = GTK_TEXT_VIEW(compose->text);
1200                         buffer = gtk_text_view_get_buffer(text);
1201                         gtk_text_buffer_get_start_iter(buffer, &start);
1202                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1203                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1204
1205                         compose_quote_fmt(compose, dummyinfo,
1206                                           body_format,
1207                                           NULL, tmp, FALSE, TRUE,
1208                                                   _("The body of the \"New message\" template has an error at line %d."));
1209                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1210                         quote_fmt_reset_vartable();
1211
1212                         g_free(tmp);
1213 #ifdef USE_ENCHANT
1214                         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1215                                 gtkaspell_highlight_all(compose->gtkaspell);
1216 #endif
1217                         mfield = BODY_FIELD_PRESENT;
1218                 }
1219
1220         }
1221         procmsg_msginfo_free( &dummyinfo );
1222
1223         if (attach_files) {
1224                 GList *curr;
1225                 AttachInfo *ainfo;
1226
1227                 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1228                         ainfo = (AttachInfo *) curr->data;
1229                         compose_attach_append(compose, ainfo->file, ainfo->file,
1230                                         ainfo->content_type, ainfo->charset);
1231                 }
1232         }
1233
1234         compose_show_first_last_header(compose, TRUE);
1235
1236         /* Set save folder */
1237         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1238                 gchar *folderidentifier;
1239
1240                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1241                 folderidentifier = folder_item_get_identifier(item);
1242                 compose_set_save_to(compose, folderidentifier);
1243                 g_free(folderidentifier);
1244         }
1245
1246         /* Place cursor according to provided input (mfield) */
1247         switch (mfield) { 
1248                 case NO_FIELD_PRESENT:
1249                         if (compose->header_last)
1250                                 gtk_widget_grab_focus(compose->header_last->entry);
1251                         break;
1252                 case TO_FIELD_PRESENT:
1253                         buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1254                         if (buf) {
1255                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1256                                 g_free(buf);
1257                         }
1258                         gtk_widget_grab_focus(compose->subject_entry);
1259                         break;
1260                 case SUBJECT_FIELD_PRESENT:
1261                         textview = GTK_TEXT_VIEW(compose->text);
1262                         if (!textview)
1263                                 break;
1264                         textbuf = gtk_text_view_get_buffer(textview);
1265                         if (!textbuf)
1266                                 break;
1267                         mark = gtk_text_buffer_get_insert(textbuf);
1268                         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1269                         gtk_text_buffer_insert(textbuf, &iter, "", -1);
1270                     /* 
1271                      * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1272                      * only defers where it comes to the variable body
1273                      * is not null. If no body is present compose->text
1274                      * will be null in which case you cannot place the
1275                      * cursor inside the component so. An empty component
1276                      * is therefore created before placing the cursor
1277                      */
1278                 case BODY_FIELD_PRESENT:
1279                         cursor_pos = quote_fmt_get_cursor_pos();
1280                         if (cursor_pos == -1)
1281                                 gtk_widget_grab_focus(compose->header_last->entry);
1282                         else
1283                                 gtk_widget_grab_focus(compose->text);
1284                         break;
1285         }
1286
1287         undo_unblock(compose->undostruct);
1288
1289         if (prefs_common.auto_exteditor)
1290                 compose_exec_ext_editor(compose);
1291
1292         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1293
1294         SCROLL_TO_CURSOR(compose);
1295
1296         compose->modified = FALSE;
1297         compose_set_title(compose);
1298
1299         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1300
1301         return compose;
1302 }
1303
1304 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1305                 gboolean override_pref, const gchar *system)
1306 {
1307         const gchar *privacy = NULL;
1308
1309         cm_return_if_fail(compose != NULL);
1310         cm_return_if_fail(account != NULL);
1311
1312         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1313                 return;
1314
1315         if (account->default_privacy_system && strlen(account->default_privacy_system))
1316                 privacy = account->default_privacy_system;
1317         else if (system)
1318                 privacy = system;
1319         else {
1320                 GSList *privacy_avail = privacy_get_system_ids();
1321                 if (privacy_avail && g_slist_length(privacy_avail)) {
1322                         privacy = (gchar *)(privacy_avail->data);
1323                 }
1324         }
1325         if (privacy != NULL) {
1326                 if (system) {
1327                         g_free(compose->privacy_system);
1328                         compose->privacy_system = NULL;
1329                         g_free(compose->encdata);
1330                         compose->encdata = NULL;
1331                 }
1332                 if (compose->privacy_system == NULL)
1333                         compose->privacy_system = g_strdup(privacy);
1334                 else if (*(compose->privacy_system) == '\0') {
1335                         g_free(compose->privacy_system);
1336                         g_free(compose->encdata);
1337                         compose->encdata = NULL;
1338                         compose->privacy_system = g_strdup(privacy);
1339                 }
1340                 compose_update_privacy_system_menu_item(compose, FALSE);
1341                 compose_use_encryption(compose, TRUE);
1342         }
1343 }       
1344
1345 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1346 {
1347         const gchar *privacy = NULL;
1348
1349         if (account->default_privacy_system && strlen(account->default_privacy_system))
1350                 privacy = account->default_privacy_system;
1351         else if (system)
1352                 privacy = system;
1353         else {
1354                 GSList *privacy_avail = privacy_get_system_ids();
1355                 if (privacy_avail && g_slist_length(privacy_avail)) {
1356                         privacy = (gchar *)(privacy_avail->data);
1357                 }
1358         }
1359
1360         if (privacy != NULL) {
1361                 if (system) {
1362                         g_free(compose->privacy_system);
1363                         compose->privacy_system = NULL;
1364                         g_free(compose->encdata);
1365                         compose->encdata = NULL;
1366                 }
1367                 if (compose->privacy_system == NULL)
1368                         compose->privacy_system = g_strdup(privacy);
1369                 compose_update_privacy_system_menu_item(compose, FALSE);
1370                 compose_use_signing(compose, TRUE);
1371         }
1372 }       
1373
1374 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1375 {
1376         MsgInfo *msginfo;
1377         guint list_len;
1378         Compose *compose = NULL;
1379         
1380         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1381
1382         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1383         cm_return_val_if_fail(msginfo != NULL, NULL);
1384
1385         list_len = g_slist_length(msginfo_list);
1386
1387         switch (mode) {
1388         case COMPOSE_REPLY:
1389         case COMPOSE_REPLY_TO_ADDRESS:
1390                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1391                               FALSE, prefs_common.default_reply_list, FALSE, body);
1392                 break;
1393         case COMPOSE_REPLY_WITH_QUOTE:
1394                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1395                         FALSE, prefs_common.default_reply_list, FALSE, body);
1396                 break;
1397         case COMPOSE_REPLY_WITHOUT_QUOTE:
1398                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1399                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1400                 break;
1401         case COMPOSE_REPLY_TO_SENDER:
1402                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1403                               FALSE, FALSE, TRUE, body);
1404                 break;
1405         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1406                 compose = compose_followup_and_reply_to(msginfo,
1407                                               COMPOSE_QUOTE_CHECK,
1408                                               FALSE, FALSE, body);
1409                 break;
1410         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1411                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1412                         FALSE, FALSE, TRUE, body);
1413                 break;
1414         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1415                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1416                         FALSE, FALSE, TRUE, NULL);
1417                 break;
1418         case COMPOSE_REPLY_TO_ALL:
1419                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1420                         TRUE, FALSE, FALSE, body);
1421                 break;
1422         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1423                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1424                         TRUE, FALSE, FALSE, body);
1425                 break;
1426         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1427                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1428                         TRUE, FALSE, FALSE, NULL);
1429                 break;
1430         case COMPOSE_REPLY_TO_LIST:
1431                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1432                         FALSE, TRUE, FALSE, body);
1433                 break;
1434         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1435                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1436                         FALSE, TRUE, FALSE, body);
1437                 break;
1438         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1439                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1440                         FALSE, TRUE, FALSE, NULL);
1441                 break;
1442         case COMPOSE_FORWARD:
1443                 if (prefs_common.forward_as_attachment) {
1444                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1445                         return compose;
1446                 } else {
1447                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1448                         return compose;
1449                 }
1450                 break;
1451         case COMPOSE_FORWARD_INLINE:
1452                 /* check if we reply to more than one Message */
1453                 if (list_len == 1) {
1454                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1455                         break;
1456                 } 
1457                 /* more messages FALL THROUGH */
1458         case COMPOSE_FORWARD_AS_ATTACH:
1459                 compose = compose_forward_multiple(NULL, msginfo_list);
1460                 break;
1461         case COMPOSE_REDIRECT:
1462                 compose = compose_redirect(NULL, msginfo, FALSE);
1463                 break;
1464         default:
1465                 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1466         }
1467         
1468         if (compose == NULL) {
1469                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1470                 return NULL;
1471         }
1472
1473         compose->rmode = mode;
1474         switch (compose->rmode) {
1475         case COMPOSE_REPLY:
1476         case COMPOSE_REPLY_WITH_QUOTE:
1477         case COMPOSE_REPLY_WITHOUT_QUOTE:
1478         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1479                 debug_print("reply mode Normal\n");
1480                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1481                 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1482                 break;
1483         case COMPOSE_REPLY_TO_SENDER:
1484         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1485         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1486                 debug_print("reply mode Sender\n");
1487                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1488                 break;
1489         case COMPOSE_REPLY_TO_ALL:
1490         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1491         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1492                 debug_print("reply mode All\n");
1493                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1494                 break;
1495         case COMPOSE_REPLY_TO_LIST:
1496         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1497         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1498                 debug_print("reply mode List\n");
1499                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1500                 break;
1501         case COMPOSE_REPLY_TO_ADDRESS:
1502                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1503                 break;
1504         default:
1505                 break;
1506         }
1507         return compose;
1508 }
1509
1510 static Compose *compose_reply(MsgInfo *msginfo,
1511                                    ComposeQuoteMode quote_mode,
1512                                    gboolean to_all,
1513                                    gboolean to_ml,
1514                                    gboolean to_sender, 
1515                                    const gchar *body)
1516 {
1517         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1518                               to_sender, FALSE, body);
1519 }
1520
1521 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1522                                    ComposeQuoteMode quote_mode,
1523                                    gboolean to_all,
1524                                    gboolean to_sender,
1525                                    const gchar *body)
1526 {
1527         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1528                               to_sender, TRUE, body);
1529 }
1530
1531 static void compose_extract_original_charset(Compose *compose)
1532 {
1533         MsgInfo *info = NULL;
1534         if (compose->replyinfo) {
1535                 info = compose->replyinfo;
1536         } else if (compose->fwdinfo) {
1537                 info = compose->fwdinfo;
1538         } else if (compose->targetinfo) {
1539                 info = compose->targetinfo;
1540         }
1541         if (info) {
1542                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1543                 MimeInfo *partinfo = mimeinfo;
1544                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1545                         partinfo = procmime_mimeinfo_next(partinfo);
1546                 if (partinfo) {
1547                         compose->orig_charset = 
1548                                 g_strdup(procmime_mimeinfo_get_parameter(
1549                                                 partinfo, "charset"));
1550                 }
1551                 procmime_mimeinfo_free_all(&mimeinfo);
1552         }
1553 }
1554
1555 #define SIGNAL_BLOCK(buffer) {                                  \
1556         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1557                                 G_CALLBACK(compose_changed_cb), \
1558                                 compose);                       \
1559         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1560                                 G_CALLBACK(text_inserted),      \
1561                                 compose);                       \
1562 }
1563
1564 #define SIGNAL_UNBLOCK(buffer) {                                \
1565         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1566                                 G_CALLBACK(compose_changed_cb), \
1567                                 compose);                       \
1568         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1569                                 G_CALLBACK(text_inserted),      \
1570                                 compose);                       \
1571 }
1572
1573 static Compose *compose_generic_reply(MsgInfo *msginfo,
1574                                   ComposeQuoteMode quote_mode,
1575                                   gboolean to_all, gboolean to_ml,
1576                                   gboolean to_sender,
1577                                   gboolean followup_and_reply_to,
1578                                   const gchar *body)
1579 {
1580         Compose *compose;
1581         PrefsAccount *account = NULL;
1582         GtkTextView *textview;
1583         GtkTextBuffer *textbuf;
1584         gboolean quote = FALSE;
1585         const gchar *qmark = NULL;
1586         const gchar *body_fmt = NULL;
1587         gchar *s_system = NULL;
1588         START_TIMING("");
1589         cm_return_val_if_fail(msginfo != NULL, NULL);
1590         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1591
1592         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1593
1594         cm_return_val_if_fail(account != NULL, NULL);
1595
1596         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1597
1598         compose->updating = TRUE;
1599
1600         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1601         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1602
1603         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1604         if (!compose->replyinfo)
1605                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1606
1607         compose_extract_original_charset(compose);
1608         
1609         if (msginfo->folder && msginfo->folder->ret_rcpt)
1610                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1611
1612         /* Set save folder */
1613         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1614                 gchar *folderidentifier;
1615
1616                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1617                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1618                 compose_set_save_to(compose, folderidentifier);
1619                 g_free(folderidentifier);
1620         }
1621
1622         if (compose_parse_header(compose, msginfo) < 0) {
1623                 compose->updating = FALSE;
1624                 compose_destroy(compose);
1625                 return NULL;
1626         }
1627
1628         /* override from name according to folder properties */
1629         if (msginfo->folder && msginfo->folder->prefs &&
1630                 msginfo->folder->prefs->reply_with_format &&
1631                 msginfo->folder->prefs->reply_override_from_format &&
1632                 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1633
1634                 gchar *tmp = NULL;
1635                 gchar *buf = NULL;
1636
1637                 /* decode \-escape sequences in the internal representation of the quote format */
1638                 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1639                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1640
1641 #ifdef USE_ENCHANT
1642                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1643                                 compose->gtkaspell);
1644 #else
1645                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1646 #endif
1647                 quote_fmt_scan_string(tmp);
1648                 quote_fmt_parse();
1649
1650                 buf = quote_fmt_get_buffer();
1651                 if (buf == NULL)
1652                         alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1653                 else
1654                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1655                 quote_fmt_reset_vartable();
1656
1657                 g_free(tmp);
1658         }
1659
1660         textview = (GTK_TEXT_VIEW(compose->text));
1661         textbuf = gtk_text_view_get_buffer(textview);
1662         compose_create_tags(textview, compose);
1663
1664         undo_block(compose->undostruct);
1665 #ifdef USE_ENCHANT
1666         compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1667         gtkaspell_block_check(compose->gtkaspell);
1668 #endif
1669
1670         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1671                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1672                 /* use the reply format of folder (if enabled), or the account's one
1673                    (if enabled) or fallback to the global reply format, which is always
1674                    enabled (even if empty), and use the relevant quotemark */
1675                 quote = TRUE;
1676                 if (msginfo->folder && msginfo->folder->prefs &&
1677                                 msginfo->folder->prefs->reply_with_format) {
1678                         qmark = msginfo->folder->prefs->reply_quotemark;
1679                         body_fmt = msginfo->folder->prefs->reply_body_format;
1680
1681                 } else if (account->reply_with_format) {
1682                         qmark = account->reply_quotemark;
1683                         body_fmt = account->reply_body_format;
1684
1685                 } else {
1686                         qmark = prefs_common.quotemark;
1687                         if (prefs_common.quotefmt && *prefs_common.quotefmt)
1688                                 body_fmt = gettext(prefs_common.quotefmt);
1689                         else
1690                                 body_fmt = "";
1691                 }
1692         }
1693
1694         if (quote) {
1695                 /* empty quotemark is not allowed */
1696                 if (qmark == NULL || *qmark == '\0')
1697                         qmark = "> ";
1698                 compose_quote_fmt(compose, compose->replyinfo,
1699                                   body_fmt, qmark, body, FALSE, TRUE,
1700                                           _("The body of the \"Reply\" template has an error at line %d."));
1701                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1702                 quote_fmt_reset_vartable();
1703         }
1704
1705         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1706                 compose_force_encryption(compose, account, FALSE, s_system);
1707         }
1708
1709         privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1710         if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1711                 compose_force_signing(compose, account, s_system);
1712         }
1713         g_free(s_system);
1714
1715         SIGNAL_BLOCK(textbuf);
1716         
1717         if (account->auto_sig)
1718                 compose_insert_sig(compose, FALSE);
1719
1720         compose_wrap_all(compose);
1721
1722 #ifdef USE_ENCHANT
1723         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1724                 gtkaspell_highlight_all(compose->gtkaspell);
1725         gtkaspell_unblock_check(compose->gtkaspell);
1726 #endif
1727         SIGNAL_UNBLOCK(textbuf);
1728         
1729         gtk_widget_grab_focus(compose->text);
1730
1731         undo_unblock(compose->undostruct);
1732
1733         if (prefs_common.auto_exteditor)
1734                 compose_exec_ext_editor(compose);
1735                 
1736         compose->modified = FALSE;
1737         compose_set_title(compose);
1738
1739         compose->updating = FALSE;
1740         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1741         SCROLL_TO_CURSOR(compose);
1742         
1743         if (compose->deferred_destroy) {
1744                 compose_destroy(compose);
1745                 return NULL;
1746         }
1747         END_TIMING();
1748
1749         return compose;
1750 }
1751
1752 #define INSERT_FW_HEADER(var, hdr) \
1753 if (msginfo->var && *msginfo->var) { \
1754         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1755         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1756         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1757 }
1758
1759 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1760                          gboolean as_attach, const gchar *body,
1761                          gboolean no_extedit,
1762                          gboolean batch)
1763 {
1764         Compose *compose;
1765         GtkTextView *textview;
1766         GtkTextBuffer *textbuf;
1767         gint cursor_pos = -1;
1768         ComposeMode mode;
1769
1770         cm_return_val_if_fail(msginfo != NULL, NULL);
1771         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1772
1773         if (!account && !(account = compose_find_account(msginfo)))
1774                 account = cur_account;
1775
1776         if (!prefs_common.forward_as_attachment)
1777                 mode = COMPOSE_FORWARD_INLINE;
1778         else
1779                 mode = COMPOSE_FORWARD;
1780         compose = compose_create(account, msginfo->folder, mode, batch);
1781
1782         compose->updating = TRUE;
1783         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1784         if (!compose->fwdinfo)
1785                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1786
1787         compose_extract_original_charset(compose);
1788
1789         if (msginfo->subject && *msginfo->subject) {
1790                 gchar *buf, *buf2, *p;
1791
1792                 buf = p = g_strdup(msginfo->subject);
1793                 p += subject_get_prefix_length(p);
1794                 memmove(buf, p, strlen(p) + 1);
1795
1796                 buf2 = g_strdup_printf("Fw: %s", buf);
1797                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1798                 
1799                 g_free(buf);
1800                 g_free(buf2);
1801         }
1802
1803         /* override from name according to folder properties */
1804         if (msginfo->folder && msginfo->folder->prefs &&
1805                 msginfo->folder->prefs->forward_with_format &&
1806                 msginfo->folder->prefs->forward_override_from_format &&
1807                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1808
1809                 gchar *tmp = NULL;
1810                 gchar *buf = NULL;
1811                 MsgInfo *full_msginfo = NULL;
1812
1813                 if (!as_attach)
1814                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1815                 if (!full_msginfo)
1816                         full_msginfo = procmsg_msginfo_copy(msginfo);
1817
1818                 /* decode \-escape sequences in the internal representation of the quote format */
1819                 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1820                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1821
1822 #ifdef USE_ENCHANT
1823                 gtkaspell_block_check(compose->gtkaspell);
1824                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1825                                 compose->gtkaspell);
1826 #else
1827                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1828 #endif
1829                 quote_fmt_scan_string(tmp);
1830                 quote_fmt_parse();
1831
1832                 buf = quote_fmt_get_buffer();
1833                 if (buf == NULL)
1834                         alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1835                 else
1836                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1837                 quote_fmt_reset_vartable();
1838
1839                 g_free(tmp);
1840                 procmsg_msginfo_free(&full_msginfo);
1841         }
1842
1843         textview = GTK_TEXT_VIEW(compose->text);
1844         textbuf = gtk_text_view_get_buffer(textview);
1845         compose_create_tags(textview, compose);
1846         
1847         undo_block(compose->undostruct);
1848         if (as_attach) {
1849                 gchar *msgfile;
1850
1851                 msgfile = procmsg_get_message_file(msginfo);
1852                 if (!is_file_exist(msgfile))
1853                         g_warning("%s: file does not exist", msgfile);
1854                 else
1855                         compose_attach_append(compose, msgfile, msgfile,
1856                                               "message/rfc822", NULL);
1857
1858                 g_free(msgfile);
1859         } else {
1860                 const gchar *qmark = NULL;
1861                 const gchar *body_fmt = NULL;
1862                 MsgInfo *full_msginfo;
1863
1864                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1865                 if (!full_msginfo)
1866                         full_msginfo = procmsg_msginfo_copy(msginfo);
1867
1868                 /* use the forward format of folder (if enabled), or the account's one
1869                    (if enabled) or fallback to the global forward format, which is always
1870                    enabled (even if empty), and use the relevant quotemark */
1871                 if (msginfo->folder && msginfo->folder->prefs &&
1872                                 msginfo->folder->prefs->forward_with_format) {
1873                         qmark = msginfo->folder->prefs->forward_quotemark;
1874                         body_fmt = msginfo->folder->prefs->forward_body_format;
1875
1876                 } else if (account->forward_with_format) {
1877                         qmark = account->forward_quotemark;
1878                         body_fmt = account->forward_body_format;
1879
1880                 } else {
1881                         qmark = prefs_common.fw_quotemark;
1882                         if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1883                                 body_fmt = gettext(prefs_common.fw_quotefmt);
1884                         else
1885                                 body_fmt = "";
1886                 }
1887
1888                 /* empty quotemark is not allowed */
1889                 if (qmark == NULL || *qmark == '\0')
1890                         qmark = "> ";
1891
1892                 compose_quote_fmt(compose, full_msginfo,
1893                                   body_fmt, qmark, body, FALSE, TRUE,
1894                                           _("The body of the \"Forward\" template has an error at line %d."));
1895                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1896                 quote_fmt_reset_vartable();
1897                 compose_attach_parts(compose, msginfo);
1898
1899                 procmsg_msginfo_free(&full_msginfo);
1900         }
1901
1902         SIGNAL_BLOCK(textbuf);
1903
1904         if (account->auto_sig)
1905                 compose_insert_sig(compose, FALSE);
1906
1907         compose_wrap_all(compose);
1908
1909 #ifdef USE_ENCHANT
1910         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1911                 gtkaspell_highlight_all(compose->gtkaspell);
1912         gtkaspell_unblock_check(compose->gtkaspell);
1913 #endif
1914         SIGNAL_UNBLOCK(textbuf);
1915         
1916         cursor_pos = quote_fmt_get_cursor_pos();
1917         if (cursor_pos == -1)
1918                 gtk_widget_grab_focus(compose->header_last->entry);
1919         else
1920                 gtk_widget_grab_focus(compose->text);
1921
1922         if (!no_extedit && prefs_common.auto_exteditor)
1923                 compose_exec_ext_editor(compose);
1924         
1925         /*save folder*/
1926         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1927                 gchar *folderidentifier;
1928
1929                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1930                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1931                 compose_set_save_to(compose, folderidentifier);
1932                 g_free(folderidentifier);
1933         }
1934
1935         undo_unblock(compose->undostruct);
1936         
1937         compose->modified = FALSE;
1938         compose_set_title(compose);
1939
1940         compose->updating = FALSE;
1941         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1942         SCROLL_TO_CURSOR(compose);
1943
1944         if (compose->deferred_destroy) {
1945                 compose_destroy(compose);
1946                 return NULL;
1947         }
1948
1949         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1950
1951         return compose;
1952 }
1953
1954 #undef INSERT_FW_HEADER
1955
1956 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1957 {
1958         Compose *compose;
1959         GtkTextView *textview;
1960         GtkTextBuffer *textbuf;
1961         GtkTextIter iter;
1962         GSList *msginfo;
1963         gchar *msgfile;
1964         gboolean single_mail = TRUE;
1965         
1966         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1967
1968         if (g_slist_length(msginfo_list) > 1)
1969                 single_mail = FALSE;
1970
1971         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1972                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1973                         return NULL;
1974
1975         /* guess account from first selected message */
1976         if (!account && 
1977             !(account = compose_find_account(msginfo_list->data)))
1978                 account = cur_account;
1979
1980         cm_return_val_if_fail(account != NULL, NULL);
1981
1982         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1983                 if (msginfo->data) {
1984                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1985                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1986                 }
1987         }
1988
1989         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1990                 g_warning("no msginfo_list");
1991                 return NULL;
1992         }
1993
1994         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1995
1996         compose->updating = TRUE;
1997
1998         /* override from name according to folder properties */
1999         if (msginfo_list->data) {
2000                 MsgInfo *msginfo = msginfo_list->data;
2001
2002                 if (msginfo->folder && msginfo->folder->prefs &&
2003                         msginfo->folder->prefs->forward_with_format &&
2004                         msginfo->folder->prefs->forward_override_from_format &&
2005                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
2006
2007                         gchar *tmp = NULL;
2008                         gchar *buf = NULL;
2009
2010                         /* decode \-escape sequences in the internal representation of the quote format */
2011                         tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2012                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2013
2014 #ifdef USE_ENCHANT
2015                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2016                                         compose->gtkaspell);
2017 #else
2018                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2019 #endif
2020                         quote_fmt_scan_string(tmp);
2021                         quote_fmt_parse();
2022
2023                         buf = quote_fmt_get_buffer();
2024                         if (buf == NULL)
2025                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2026                         else
2027                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2028                         quote_fmt_reset_vartable();
2029
2030                         g_free(tmp);
2031                 }
2032         }
2033
2034         textview = GTK_TEXT_VIEW(compose->text);
2035         textbuf = gtk_text_view_get_buffer(textview);
2036         compose_create_tags(textview, compose);
2037         
2038         undo_block(compose->undostruct);
2039         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2040                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2041
2042                 if (!is_file_exist(msgfile))
2043                         g_warning("%s: file does not exist", msgfile);
2044                 else
2045                         compose_attach_append(compose, msgfile, msgfile,
2046                                 "message/rfc822", NULL);
2047                 g_free(msgfile);
2048         }
2049         
2050         if (single_mail) {
2051                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2052                 if (info->subject && *info->subject) {
2053                         gchar *buf, *buf2, *p;
2054
2055                         buf = p = g_strdup(info->subject);
2056                         p += subject_get_prefix_length(p);
2057                         memmove(buf, p, strlen(p) + 1);
2058
2059                         buf2 = g_strdup_printf("Fw: %s", buf);
2060                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2061
2062                         g_free(buf);
2063                         g_free(buf2);
2064                 }
2065         } else {
2066                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2067                         _("Fw: multiple emails"));
2068         }
2069
2070         SIGNAL_BLOCK(textbuf);
2071         
2072         if (account->auto_sig)
2073                 compose_insert_sig(compose, FALSE);
2074
2075         compose_wrap_all(compose);
2076
2077         SIGNAL_UNBLOCK(textbuf);
2078         
2079         gtk_text_buffer_get_start_iter(textbuf, &iter);
2080         gtk_text_buffer_place_cursor(textbuf, &iter);
2081
2082         if (prefs_common.auto_exteditor)
2083                 compose_exec_ext_editor(compose);
2084
2085         gtk_widget_grab_focus(compose->header_last->entry);
2086         undo_unblock(compose->undostruct);
2087         compose->modified = FALSE;
2088         compose_set_title(compose);
2089
2090         compose->updating = FALSE;
2091         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2092         SCROLL_TO_CURSOR(compose);
2093
2094         if (compose->deferred_destroy) {
2095                 compose_destroy(compose);
2096                 return NULL;
2097         }
2098
2099         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2100
2101         return compose;
2102 }
2103
2104 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2105 {
2106         GtkTextIter start = *iter;
2107         GtkTextIter end_iter;
2108         int start_pos = gtk_text_iter_get_offset(&start);
2109         gchar *str = NULL;
2110         if (!compose->account->sig_sep)
2111                 return FALSE;
2112         
2113         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2114                 start_pos+strlen(compose->account->sig_sep));
2115
2116         /* check sig separator */
2117         str = gtk_text_iter_get_text(&start, &end_iter);
2118         if (!strcmp(str, compose->account->sig_sep)) {
2119                 gchar *tmp = NULL;
2120                 /* check end of line (\n) */
2121                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2122                         start_pos+strlen(compose->account->sig_sep));
2123                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2124                         start_pos+strlen(compose->account->sig_sep)+1);
2125                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2126                 if (!strcmp(tmp,"\n")) {
2127                         g_free(str);
2128                         g_free(tmp);
2129                         return TRUE;
2130                 }
2131                 g_free(tmp);    
2132         }
2133         g_free(str);
2134
2135         return FALSE;
2136 }
2137
2138 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2139 {
2140         FolderUpdateData *hookdata = (FolderUpdateData *)source;
2141         Compose *compose = (Compose *)data;
2142         FolderItem *old_item = NULL;
2143         FolderItem *new_item = NULL;
2144         gchar *old_id, *new_id;
2145
2146         if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2147          && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2148                 return FALSE;
2149
2150         old_item = hookdata->item;
2151         new_item = hookdata->item2;
2152
2153         old_id = folder_item_get_identifier(old_item);
2154         new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2155
2156         if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2157                 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2158                 compose->targetinfo->folder = new_item;
2159         }
2160
2161         if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2162                 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2163                 compose->replyinfo->folder = new_item;
2164         }
2165
2166         if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2167                 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2168                 compose->fwdinfo->folder = new_item;
2169         }
2170
2171         g_free(old_id);
2172         g_free(new_id);
2173         return FALSE;
2174 }
2175
2176 static void compose_colorize_signature(Compose *compose)
2177 {
2178         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2179         GtkTextIter iter;
2180         GtkTextIter end_iter;
2181         gtk_text_buffer_get_start_iter(buffer, &iter);
2182         while (gtk_text_iter_forward_line(&iter))
2183                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2184                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2185                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2186                 }
2187 }
2188
2189 #define BLOCK_WRAP() {                                                  \
2190         prev_autowrap = compose->autowrap;                              \
2191         buffer = gtk_text_view_get_buffer(                              \
2192                                         GTK_TEXT_VIEW(compose->text));  \
2193         compose->autowrap = FALSE;                                      \
2194                                                                         \
2195         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2196                                 G_CALLBACK(compose_changed_cb),         \
2197                                 compose);                               \
2198         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2199                                 G_CALLBACK(text_inserted),              \
2200                                 compose);                               \
2201 }
2202 #define UNBLOCK_WRAP() {                                                        \
2203         compose->autowrap = prev_autowrap;                                      \
2204         if (compose->autowrap) {                                                \
2205                 gint old = compose->draft_timeout_tag;                          \
2206                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;   \
2207                 compose_wrap_all(compose);                                      \
2208                 compose->draft_timeout_tag = old;                               \
2209         }                                                                       \
2210                                                                                 \
2211         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
2212                                 G_CALLBACK(compose_changed_cb),                 \
2213                                 compose);                                       \
2214         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
2215                                 G_CALLBACK(text_inserted),                      \
2216                                 compose);                                       \
2217 }
2218
2219 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2220 {
2221         Compose *compose = NULL;
2222         PrefsAccount *account = NULL;
2223         GtkTextView *textview;
2224         GtkTextBuffer *textbuf;
2225         GtkTextMark *mark;
2226         GtkTextIter iter;
2227         FILE *fp;
2228         gboolean use_signing = FALSE;
2229         gboolean use_encryption = FALSE;
2230         gchar *privacy_system = NULL;
2231         int priority = PRIORITY_NORMAL;
2232         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2233         gboolean autowrap = prefs_common.autowrap;
2234         gboolean autoindent = prefs_common.auto_indent;
2235         HeaderEntry *manual_headers = NULL;
2236
2237         cm_return_val_if_fail(msginfo != NULL, NULL);
2238         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2239
2240         if (compose_put_existing_to_front(msginfo)) {
2241                 return NULL;
2242         }
2243
2244         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2245             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2246             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2247                 gchar *queueheader_buf = NULL;
2248                 gint id, param;
2249
2250                 /* Select Account from queue headers */
2251                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2252                                                                                         "X-Claws-Account-Id:")) {
2253                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2254                         account = account_find_from_id(id);
2255                         g_free(queueheader_buf);
2256                 }
2257                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2258                                                                                         "X-Sylpheed-Account-Id:")) {
2259                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2260                         account = account_find_from_id(id);
2261                         g_free(queueheader_buf);
2262                 }
2263                 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2264                                                                                         "NAID:")) {
2265                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2266                         account = account_find_from_id(id);
2267                         g_free(queueheader_buf);
2268                 }
2269                 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2270                                                                                         "MAID:")) {
2271                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2272                         account = account_find_from_id(id);
2273                         g_free(queueheader_buf);
2274                 }
2275                 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2276                                                                                         "S:")) {
2277                         account = account_find_from_address(queueheader_buf, FALSE);
2278                         g_free(queueheader_buf);
2279                 }
2280                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2281                                                                                 "X-Claws-Sign:")) {
2282                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2283                         use_signing = param;
2284                         g_free(queueheader_buf);
2285                 }
2286                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2287                                                                                 "X-Sylpheed-Sign:")) {
2288                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2289                         use_signing = param;
2290                         g_free(queueheader_buf);
2291                 }
2292                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2293                                                                                 "X-Claws-Encrypt:")) {
2294                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2295                         use_encryption = param;
2296                         g_free(queueheader_buf);
2297                 }
2298                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2299                                                                                 "X-Sylpheed-Encrypt:")) {
2300                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2301                         use_encryption = param;
2302                         g_free(queueheader_buf);
2303                 }
2304                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2305                                                                                 "X-Claws-Auto-Wrapping:")) {
2306                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2307                         autowrap = param;
2308                         g_free(queueheader_buf);
2309                 }
2310                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2311                                                                                 "X-Claws-Auto-Indent:")) {
2312                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2313                         autoindent = param;
2314                         g_free(queueheader_buf);
2315                 }
2316                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2317                                         "X-Claws-Privacy-System:")) {
2318                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2319                         g_free(queueheader_buf);
2320                 }
2321                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2322                                         "X-Sylpheed-Privacy-System:")) {
2323                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2324                         g_free(queueheader_buf);
2325                 }
2326                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2327                                                                                 "X-Priority: ")) {
2328                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2329                         priority = param;
2330                         g_free(queueheader_buf);
2331                 }
2332                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2333                                                                                         "RMID:")) {
2334                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2335                         if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2336                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2337                                 if (orig_item != NULL) {
2338                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2339                                 }
2340                                 g_strfreev(tokens);
2341                         }
2342                         g_free(queueheader_buf);
2343                 }
2344                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2345                                                                                 "FMID:")) {
2346                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2347                         if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2348                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2349                                 if (orig_item != NULL) {
2350                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2351                                 }
2352                                 g_strfreev(tokens);
2353                         }
2354                         g_free(queueheader_buf);
2355                 }
2356                 /* Get manual headers */
2357                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2358                                                                                         "X-Claws-Manual-Headers:")) {
2359                         gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2360                         if (listmh && *listmh != '\0') {
2361                                 debug_print("Got manual headers: %s\n", listmh);
2362                                 manual_headers = procheader_entries_from_str(listmh);
2363                                 g_free(listmh);
2364                         }
2365                         g_free(queueheader_buf);
2366                 }
2367         } else {
2368                 account = msginfo->folder->folder->account;
2369         }
2370
2371         if (!account && prefs_common.reedit_account_autosel) {
2372                 gchar *from = NULL;
2373                 if (!procheader_get_header_from_msginfo(msginfo, &from, "FROM:")) {
2374                         extract_address(from);
2375                         account = account_find_from_address(from, FALSE);
2376                         g_free(from);
2377                 }
2378         }
2379         if (!account) {
2380                 account = cur_account;
2381         }
2382         cm_return_val_if_fail(account != NULL, NULL);
2383
2384         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2385
2386         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2387         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2388         compose->autowrap = autowrap;
2389         compose->replyinfo = replyinfo;
2390         compose->fwdinfo = fwdinfo;
2391
2392         compose->updating = TRUE;
2393         compose->priority = priority;
2394
2395         if (privacy_system != NULL) {
2396                 compose->privacy_system = privacy_system;
2397                 compose_use_signing(compose, use_signing);
2398                 compose_use_encryption(compose, use_encryption);
2399                 compose_update_privacy_system_menu_item(compose, FALSE);
2400         } else {
2401                 activate_privacy_system(compose, account, FALSE);
2402         }
2403
2404         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2405
2406         compose_extract_original_charset(compose);
2407
2408         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2409             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2410             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2411                 gchar *queueheader_buf = NULL;
2412
2413                 /* Set message save folder */
2414                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "SCF:")) {
2415                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2416                         compose_set_save_to(compose, &queueheader_buf[4]);
2417                         g_free(queueheader_buf);
2418                 }
2419                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "RRCPT:")) {
2420                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2421                         if (active) {
2422                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2423                         }
2424                         g_free(queueheader_buf);
2425                 }
2426         }
2427         
2428         if (compose_parse_header(compose, msginfo) < 0) {
2429                 compose->updating = FALSE;
2430                 compose_destroy(compose);
2431                 return NULL;
2432         }
2433         compose_reedit_set_entry(compose, msginfo);
2434
2435         textview = GTK_TEXT_VIEW(compose->text);
2436         textbuf = gtk_text_view_get_buffer(textview);
2437         compose_create_tags(textview, compose);
2438
2439         mark = gtk_text_buffer_get_insert(textbuf);
2440         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2441
2442         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2443                                         G_CALLBACK(compose_changed_cb),
2444                                         compose);
2445         
2446         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2447                 fp = procmime_get_first_encrypted_text_content(msginfo);
2448                 if (fp) {
2449                         compose_force_encryption(compose, account, TRUE, NULL);
2450                 }
2451         } else {
2452                 fp = procmime_get_first_text_content(msginfo);
2453         }
2454         if (fp == NULL) {
2455                 g_warning("Can't get text part");
2456         }
2457
2458         if (fp != NULL) {
2459                 gchar buf[BUFFSIZE];
2460                 gboolean prev_autowrap;
2461                 GtkTextBuffer *buffer;
2462                 BLOCK_WRAP();
2463                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2464                         strcrchomp(buf);
2465                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2466                 }
2467                 UNBLOCK_WRAP();
2468                 fclose(fp);
2469         }
2470         
2471         compose_attach_parts(compose, msginfo);
2472
2473         compose_colorize_signature(compose);
2474
2475         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2476                                         G_CALLBACK(compose_changed_cb),
2477                                         compose);
2478
2479         if (manual_headers != NULL) {
2480                 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2481                         procheader_entries_free(manual_headers);
2482                         compose->updating = FALSE;
2483                         compose_destroy(compose);
2484                         return NULL;
2485                 }
2486                 procheader_entries_free(manual_headers);
2487         }
2488
2489         gtk_widget_grab_focus(compose->text);
2490
2491         if (prefs_common.auto_exteditor) {
2492                 compose_exec_ext_editor(compose);
2493         }
2494         compose->modified = FALSE;
2495         compose_set_title(compose);
2496
2497         compose->updating = FALSE;
2498         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2499         SCROLL_TO_CURSOR(compose);
2500
2501         if (compose->deferred_destroy) {
2502                 compose_destroy(compose);
2503                 return NULL;
2504         }
2505         
2506         compose->sig_str = account_get_signature_str(compose->account);
2507         
2508         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2509
2510         return compose;
2511 }
2512
2513 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2514                                                  gboolean batch)
2515 {
2516         Compose *compose;
2517         gchar *filename;
2518         FolderItem *item;
2519
2520         cm_return_val_if_fail(msginfo != NULL, NULL);
2521
2522         if (!account)
2523                 account = account_get_reply_account(msginfo,
2524                                         prefs_common.reply_account_autosel);
2525         cm_return_val_if_fail(account != NULL, NULL);
2526
2527         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2528
2529         compose->updating = TRUE;
2530
2531         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2532         compose->replyinfo = NULL;
2533         compose->fwdinfo = NULL;
2534
2535         compose_show_first_last_header(compose, TRUE);
2536
2537         gtk_widget_grab_focus(compose->header_last->entry);
2538
2539         filename = procmsg_get_message_file(msginfo);
2540
2541         if (filename == NULL) {
2542                 compose->updating = FALSE;
2543                 compose_destroy(compose);
2544
2545                 return NULL;
2546         }
2547
2548         compose->redirect_filename = filename;
2549         
2550         /* Set save folder */
2551         item = msginfo->folder;
2552         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2553                 gchar *folderidentifier;
2554
2555                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2556                 folderidentifier = folder_item_get_identifier(item);
2557                 compose_set_save_to(compose, folderidentifier);
2558                 g_free(folderidentifier);
2559         }
2560
2561         compose_attach_parts(compose, msginfo);
2562
2563         if (msginfo->subject)
2564                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2565                                    msginfo->subject);
2566         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2567
2568         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2569                                           _("The body of the \"Redirect\" template has an error at line %d."));
2570         quote_fmt_reset_vartable();
2571         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2572
2573         compose_colorize_signature(compose);
2574
2575         
2576         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2577         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2578         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2579
2580         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2581         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2582         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2583         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2584         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2585         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2586         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2587         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2588         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2589         
2590         if (compose->toolbar->draft_btn)
2591                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2592         if (compose->toolbar->insert_btn)
2593                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2594         if (compose->toolbar->attach_btn)
2595                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2596         if (compose->toolbar->sig_btn)
2597                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2598         if (compose->toolbar->exteditor_btn)
2599                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2600         if (compose->toolbar->linewrap_current_btn)
2601                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2602         if (compose->toolbar->linewrap_all_btn)
2603                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2604
2605         compose->modified = FALSE;
2606         compose_set_title(compose);
2607         compose->updating = FALSE;
2608         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2609         SCROLL_TO_CURSOR(compose);
2610
2611         if (compose->deferred_destroy) {
2612                 compose_destroy(compose);
2613                 return NULL;
2614         }
2615         
2616         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2617
2618         return compose;
2619 }
2620
2621 const GList *compose_get_compose_list(void)
2622 {
2623         return compose_list;
2624 }
2625
2626 void compose_entry_append(Compose *compose, const gchar *address,
2627                           ComposeEntryType type, ComposePrefType pref_type)
2628 {
2629         const gchar *header;
2630         gchar *cur, *begin;
2631         gboolean in_quote = FALSE;
2632         if (!address || *address == '\0') return;
2633
2634         switch (type) {
2635         case COMPOSE_CC:
2636                 header = N_("Cc:");
2637                 break;
2638         case COMPOSE_BCC:
2639                 header = N_("Bcc:");
2640                 break;
2641         case COMPOSE_REPLYTO:
2642                 header = N_("Reply-To:");
2643                 break;
2644         case COMPOSE_NEWSGROUPS:
2645                 header = N_("Newsgroups:");
2646                 break;
2647         case COMPOSE_FOLLOWUPTO:
2648                 header = N_( "Followup-To:");
2649                 break;
2650         case COMPOSE_INREPLYTO:
2651                 header = N_( "In-Reply-To:");
2652                 break;
2653         case COMPOSE_TO:
2654         default:
2655                 header = N_("To:");
2656                 break;
2657         }
2658         header = prefs_common_translated_header_name(header);
2659         
2660         cur = begin = (gchar *)address;
2661         
2662         /* we separate the line by commas, but not if we're inside a quoted
2663          * string */
2664         while (*cur != '\0') {
2665                 if (*cur == '"') 
2666                         in_quote = !in_quote;
2667                 if (*cur == ',' && !in_quote) {
2668                         gchar *tmp = g_strdup(begin);
2669                         gchar *o_tmp = tmp;
2670                         tmp[cur-begin]='\0';
2671                         cur++;
2672                         begin = cur;
2673                         while (*tmp == ' ' || *tmp == '\t')
2674                                 tmp++;
2675                         compose_add_header_entry(compose, header, tmp, pref_type);
2676                         compose_entry_indicate(compose, tmp);
2677                         g_free(o_tmp);
2678                         continue;
2679                 }
2680                 cur++;
2681         }
2682         if (begin < cur) {
2683                 gchar *tmp = g_strdup(begin);
2684                 gchar *o_tmp = tmp;
2685                 tmp[cur-begin]='\0';
2686                 while (*tmp == ' ' || *tmp == '\t')
2687                         tmp++;
2688                 compose_add_header_entry(compose, header, tmp, pref_type);
2689                 compose_entry_indicate(compose, tmp);
2690                 g_free(o_tmp);          
2691         }
2692 }
2693
2694 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2695 {
2696         GSList *h_list;
2697         GtkEntry *entry;
2698                 
2699         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2700                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2701                 if (gtk_entry_get_text(entry) && 
2702                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2703                                 gtk_widget_modify_base(
2704                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2705                                         GTK_STATE_NORMAL, &default_header_bgcolor);
2706                                 gtk_widget_modify_text(
2707                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2708                                         GTK_STATE_NORMAL, &default_header_color);
2709                 }
2710         }
2711 }
2712
2713 void compose_toolbar_cb(gint action, gpointer data)
2714 {
2715         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2716         Compose *compose = (Compose*)toolbar_item->parent;
2717         
2718         cm_return_if_fail(compose != NULL);
2719
2720         switch(action) {
2721         case A_SEND:
2722                 compose_send_cb(NULL, compose);
2723                 break;
2724         case A_SEND_LATER:
2725                 compose_send_later_cb(NULL, compose);
2726                 break;
2727         case A_DRAFT:
2728                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2729                 break;
2730         case A_INSERT:
2731                 compose_insert_file_cb(NULL, compose);
2732                 break;
2733         case A_ATTACH:
2734                 compose_attach_cb(NULL, compose);
2735                 break;
2736         case A_SIG:
2737                 compose_insert_sig(compose, FALSE);
2738                 break;
2739         case A_REP_SIG:
2740                 compose_insert_sig(compose, TRUE);
2741                 break;
2742         case A_EXTEDITOR:
2743                 compose_ext_editor_cb(NULL, compose);
2744                 break;
2745         case A_LINEWRAP_CURRENT:
2746                 compose_beautify_paragraph(compose, NULL, TRUE);
2747                 break;
2748         case A_LINEWRAP_ALL:
2749                 compose_wrap_all_full(compose, TRUE);
2750                 break;
2751         case A_ADDRBOOK:
2752                 compose_address_cb(NULL, compose);
2753                 break;
2754 #ifdef USE_ENCHANT
2755         case A_CHECK_SPELLING:
2756                 compose_check_all(NULL, compose);
2757                 break;
2758 #endif
2759         default:
2760                 break;
2761         }
2762 }
2763
2764 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2765 {
2766         gchar *to = NULL;
2767         gchar *cc = NULL;
2768         gchar *bcc = NULL;
2769         gchar *subject = NULL;
2770         gchar *body = NULL;
2771         gchar *temp = NULL;
2772         gsize  len = 0;
2773         gchar **attach = NULL;
2774         gchar *inreplyto = NULL;
2775         MailField mfield = NO_FIELD_PRESENT;
2776
2777         /* get mailto parts but skip from */
2778         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2779
2780         if (to) {
2781                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2782                 mfield = TO_FIELD_PRESENT;
2783         }
2784         if (cc)
2785                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2786         if (bcc)
2787                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2788         if (subject) {
2789                 if (!g_utf8_validate (subject, -1, NULL)) {
2790                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2791                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2792                         g_free(temp);
2793                 } else {
2794                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2795                 }
2796                 mfield = SUBJECT_FIELD_PRESENT;
2797         }
2798         if (body) {
2799                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2800                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2801                 GtkTextMark *mark;
2802                 GtkTextIter iter;
2803                 gboolean prev_autowrap = compose->autowrap;
2804
2805                 compose->autowrap = FALSE;
2806
2807                 mark = gtk_text_buffer_get_insert(buffer);
2808                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2809
2810                 if (!g_utf8_validate (body, -1, NULL)) {
2811                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2812                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2813                         g_free(temp);
2814                 } else {
2815                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2816                 }
2817                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2818
2819                 compose->autowrap = prev_autowrap;
2820                 if (compose->autowrap)
2821                         compose_wrap_all(compose);
2822                 mfield = BODY_FIELD_PRESENT;
2823         }
2824
2825         if (attach) {
2826                 gint i = 0, att = 0;
2827                 gchar *warn_files = NULL;
2828                 while (attach[i] != NULL) {
2829                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2830                         if (utf8_filename) {
2831                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2832                                         gchar *tmp = g_strdup_printf("%s%s\n",
2833                                                         warn_files?warn_files:"",
2834                                                         utf8_filename);
2835                                         g_free(warn_files);
2836                                         warn_files = tmp;
2837                                         att++;
2838                                 }
2839                                 g_free(utf8_filename);
2840                         } else {
2841                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2842                         }
2843                         i++;
2844                 }
2845                 if (warn_files) {
2846                         alertpanel_notice(ngettext(
2847                         "The following file has been attached: \n%s",
2848                         "The following files have been attached: \n%s", att), warn_files);
2849                         g_free(warn_files);
2850                 }
2851         }
2852         if (inreplyto)
2853                 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2854
2855         g_free(to);
2856         g_free(cc);
2857         g_free(bcc);
2858         g_free(subject);
2859         g_free(body);
2860         g_strfreev(attach);
2861         g_free(inreplyto);
2862         
2863         return mfield;
2864 }
2865
2866 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2867 {
2868         static HeaderEntry hentry[] = {
2869                                        {"Reply-To:",    NULL, TRUE },
2870                                        {"Cc:",          NULL, TRUE },
2871                                        {"References:",  NULL, FALSE },
2872                                        {"Bcc:",         NULL, TRUE },
2873                                        {"Newsgroups:",  NULL, TRUE },
2874                                        {"Followup-To:", NULL, TRUE },
2875                                        {"List-Post:",   NULL, FALSE },
2876                                        {"X-Priority:",  NULL, FALSE },
2877                                        {NULL,           NULL, FALSE }
2878         };
2879
2880         enum
2881         {
2882                 H_REPLY_TO    = 0,
2883                 H_CC          = 1,
2884                 H_REFERENCES  = 2,
2885                 H_BCC         = 3,
2886                 H_NEWSGROUPS  = 4,
2887                 H_FOLLOWUP_TO = 5,
2888                 H_LIST_POST   = 6,
2889                 H_X_PRIORITY  = 7
2890         };
2891
2892         FILE *fp;
2893
2894         cm_return_val_if_fail(msginfo != NULL, -1);
2895
2896         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2897         procheader_get_header_fields(fp, hentry);
2898         fclose(fp);
2899
2900         if (hentry[H_REPLY_TO].body != NULL) {
2901                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2902                         compose->replyto =
2903                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2904                                                    NULL, TRUE);
2905                 }
2906                 g_free(hentry[H_REPLY_TO].body);
2907                 hentry[H_REPLY_TO].body = NULL;
2908         }
2909         if (hentry[H_CC].body != NULL) {
2910                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2911                 g_free(hentry[H_CC].body);
2912                 hentry[H_CC].body = NULL;
2913         }
2914         if (hentry[H_REFERENCES].body != NULL) {
2915                 if (compose->mode == COMPOSE_REEDIT)
2916                         compose->references = hentry[H_REFERENCES].body;
2917                 else {
2918                         compose->references = compose_parse_references
2919                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2920                         g_free(hentry[H_REFERENCES].body);
2921                 }
2922                 hentry[H_REFERENCES].body = NULL;
2923         }
2924         if (hentry[H_BCC].body != NULL) {
2925                 if (compose->mode == COMPOSE_REEDIT)
2926                         compose->bcc =
2927                                 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2928                 g_free(hentry[H_BCC].body);
2929                 hentry[H_BCC].body = NULL;
2930         }
2931         if (hentry[H_NEWSGROUPS].body != NULL) {
2932                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2933                 hentry[H_NEWSGROUPS].body = NULL;
2934         }
2935         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2936                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2937                         compose->followup_to =
2938                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2939                                                    NULL, TRUE);
2940                 }
2941                 g_free(hentry[H_FOLLOWUP_TO].body);
2942                 hentry[H_FOLLOWUP_TO].body = NULL;
2943         }
2944         if (hentry[H_LIST_POST].body != NULL) {
2945                 gchar *to = NULL, *start = NULL;
2946
2947                 extract_address(hentry[H_LIST_POST].body);
2948                 if (hentry[H_LIST_POST].body[0] != '\0') {
2949                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2950                         
2951                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2952                                         NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2953
2954                         if (to) {
2955                                 g_free(compose->ml_post);
2956                                 compose->ml_post = to;
2957                         }
2958                 }
2959                 g_free(hentry[H_LIST_POST].body);
2960                 hentry[H_LIST_POST].body = NULL;
2961         }
2962
2963         /* CLAWS - X-Priority */
2964         if (compose->mode == COMPOSE_REEDIT)
2965                 if (hentry[H_X_PRIORITY].body != NULL) {
2966                         gint priority;
2967                         
2968                         priority = atoi(hentry[H_X_PRIORITY].body);
2969                         g_free(hentry[H_X_PRIORITY].body);
2970                         
2971                         hentry[H_X_PRIORITY].body = NULL;
2972                         
2973                         if (priority < PRIORITY_HIGHEST || 
2974                             priority > PRIORITY_LOWEST)
2975                                 priority = PRIORITY_NORMAL;
2976                         
2977                         compose->priority =  priority;
2978                 }
2979  
2980         if (compose->mode == COMPOSE_REEDIT) {
2981                 if (msginfo->inreplyto && *msginfo->inreplyto)
2982                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2983
2984                 if (msginfo->msgid && *msginfo->msgid)
2985                         compose->msgid = g_strdup(msginfo->msgid);
2986         } else {
2987                 if (msginfo->msgid && *msginfo->msgid)
2988                         compose->inreplyto = g_strdup(msginfo->msgid);
2989
2990                 if (!compose->references) {
2991                         if (msginfo->msgid && *msginfo->msgid) {
2992                                 if (msginfo->inreplyto && *msginfo->inreplyto)
2993                                         compose->references =
2994                                                 g_strdup_printf("<%s>\n\t<%s>",
2995                                                                 msginfo->inreplyto,
2996                                                                 msginfo->msgid);
2997                                 else
2998                                         compose->references =
2999                                                 g_strconcat("<", msginfo->msgid, ">",
3000                                                             NULL);
3001                         } else if (msginfo->inreplyto && *msginfo->inreplyto) {
3002                                 compose->references =
3003                                         g_strconcat("<", msginfo->inreplyto, ">",
3004                                                     NULL);
3005                         }
3006                 }
3007         }
3008
3009         return 0;
3010 }
3011
3012 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3013 {
3014         FILE *fp;
3015         HeaderEntry *he;
3016
3017         cm_return_val_if_fail(msginfo != NULL, -1);
3018
3019         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3020         procheader_get_header_fields(fp, entries);
3021         fclose(fp);
3022
3023         he = entries;
3024         while (he != NULL && he->name != NULL) {
3025                 GtkTreeIter iter;
3026                 GtkListStore *model = NULL;
3027
3028                 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3029                 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3030                 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3031                 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3032                 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3033                 ++he;
3034         }
3035
3036         return 0;
3037 }
3038
3039 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3040 {
3041         GSList *ref_id_list, *cur;
3042         GString *new_ref;
3043         gchar *new_ref_str;
3044
3045         ref_id_list = references_list_append(NULL, ref);
3046         if (!ref_id_list) return NULL;
3047         if (msgid && *msgid)
3048                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3049
3050         for (;;) {
3051                 gint len = 0;
3052
3053                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3054                         /* "<" + Message-ID + ">" + CR+LF+TAB */
3055                         len += strlen((gchar *)cur->data) + 5;
3056
3057                 if (len > MAX_REFERENCES_LEN) {
3058                         /* remove second message-ID */
3059                         if (ref_id_list && ref_id_list->next &&
3060                             ref_id_list->next->next) {
3061                                 g_free(ref_id_list->next->data);
3062                                 ref_id_list = g_slist_remove
3063                                         (ref_id_list, ref_id_list->next->data);
3064                         } else {
3065                                 slist_free_strings_full(ref_id_list);
3066                                 return NULL;
3067                         }
3068                 } else
3069                         break;
3070         }
3071
3072         new_ref = g_string_new("");
3073         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3074                 if (new_ref->len > 0)
3075                         g_string_append(new_ref, "\n\t");
3076                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3077         }
3078
3079         slist_free_strings_full(ref_id_list);
3080
3081         new_ref_str = new_ref->str;
3082         g_string_free(new_ref, FALSE);
3083
3084         return new_ref_str;
3085 }
3086
3087 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3088                                 const gchar *fmt, const gchar *qmark,
3089                                 const gchar *body, gboolean rewrap,
3090                                 gboolean need_unescape,
3091                                 const gchar *err_msg)
3092 {
3093         MsgInfo* dummyinfo = NULL;
3094         gchar *quote_str = NULL;
3095         gchar *buf;
3096         gboolean prev_autowrap;
3097         const gchar *trimmed_body = body;
3098         gint cursor_pos = -1;
3099         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3100         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3101         GtkTextIter iter;
3102         GtkTextMark *mark;
3103         
3104
3105         SIGNAL_BLOCK(buffer);
3106
3107         if (!msginfo) {
3108                 dummyinfo = compose_msginfo_new_from_compose(compose);
3109                 msginfo = dummyinfo;
3110         }
3111
3112         if (qmark != NULL) {
3113 #ifdef USE_ENCHANT
3114                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3115                                 compose->gtkaspell);
3116 #else
3117                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3118 #endif
3119                 quote_fmt_scan_string(qmark);
3120                 quote_fmt_parse();
3121
3122                 buf = quote_fmt_get_buffer();
3123                 if (buf == NULL)
3124                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3125                 else
3126                         Xstrdup_a(quote_str, buf, goto error)
3127         }
3128
3129         if (fmt && *fmt != '\0') {
3130
3131                 if (trimmed_body)
3132                         while (*trimmed_body == '\n')
3133                                 trimmed_body++;
3134
3135 #ifdef USE_ENCHANT
3136                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3137                                 compose->gtkaspell);
3138 #else
3139                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3140 #endif
3141                 if (need_unescape) {
3142                         gchar *tmp = NULL;
3143
3144                         /* decode \-escape sequences in the internal representation of the quote format */
3145                         tmp = g_malloc(strlen(fmt)+1);
3146                         pref_get_unescaped_pref(tmp, fmt);
3147                         quote_fmt_scan_string(tmp);
3148                         quote_fmt_parse();
3149                         g_free(tmp);
3150                 } else {
3151                         quote_fmt_scan_string(fmt);
3152                         quote_fmt_parse();
3153                 }
3154
3155                 buf = quote_fmt_get_buffer();
3156                 if (buf == NULL) {
3157                         gint line = quote_fmt_get_line();
3158                         alertpanel_error(err_msg, line);
3159                         goto error;
3160                 }
3161         } else
3162                 buf = "";
3163
3164         prev_autowrap = compose->autowrap;
3165         compose->autowrap = FALSE;
3166
3167         mark = gtk_text_buffer_get_insert(buffer);
3168         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3169         if (g_utf8_validate(buf, -1, NULL)) { 
3170                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3171         } else {
3172                 gchar *tmpout = NULL;
3173                 tmpout = conv_codeset_strdup
3174                         (buf, conv_get_locale_charset_str_no_utf8(),
3175                          CS_INTERNAL);
3176                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3177                         g_free(tmpout);
3178                         tmpout = g_malloc(strlen(buf)*2+1);
3179                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3180                 }
3181                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3182                 g_free(tmpout);
3183         }
3184
3185         cursor_pos = quote_fmt_get_cursor_pos();
3186         if (cursor_pos == -1)
3187                 cursor_pos = gtk_text_iter_get_offset(&iter);
3188         compose->set_cursor_pos = cursor_pos;
3189
3190         gtk_text_buffer_get_start_iter(buffer, &iter);
3191         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3192         gtk_text_buffer_place_cursor(buffer, &iter);
3193
3194         compose->autowrap = prev_autowrap;
3195         if (compose->autowrap && rewrap)
3196                 compose_wrap_all(compose);
3197
3198         goto ok;
3199
3200 error:
3201         buf = NULL;
3202 ok:
3203         SIGNAL_UNBLOCK(buffer);
3204
3205         procmsg_msginfo_free( &dummyinfo );
3206
3207         return buf;
3208 }
3209
3210 /* if ml_post is of type addr@host and from is of type
3211  * addr-anything@host, return TRUE
3212  */
3213 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3214 {
3215         gchar *left_ml = NULL;
3216         gchar *right_ml = NULL;
3217         gchar *left_from = NULL;
3218         gchar *right_from = NULL;
3219         gboolean result = FALSE;
3220         
3221         if (!ml_post || !from)
3222                 return FALSE;
3223         
3224         left_ml = g_strdup(ml_post);
3225         if (strstr(left_ml, "@")) {
3226                 right_ml = strstr(left_ml, "@")+1;
3227                 *(strstr(left_ml, "@")) = '\0';
3228         }
3229         
3230         left_from = g_strdup(from);
3231         if (strstr(left_from, "@")) {
3232                 right_from = strstr(left_from, "@")+1;
3233                 *(strstr(left_from, "@")) = '\0';
3234         }
3235         
3236         if (right_ml && right_from
3237         &&  !strncmp(left_from, left_ml, strlen(left_ml))
3238         &&  !strcmp(right_from, right_ml)) {
3239                 result = TRUE;
3240         }
3241         g_free(left_ml);
3242         g_free(left_from);
3243         
3244         return result;
3245 }
3246
3247 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3248                                      gboolean respect_default_to)
3249 {
3250         if (!compose)
3251                 return;
3252         if (!folder || !folder->prefs)
3253                 return;
3254
3255         if (respect_default_to && folder->prefs->enable_default_to) {
3256                 compose_entry_append(compose, folder->prefs->default_to,
3257                                         COMPOSE_TO, PREF_FOLDER);
3258                 compose_entry_indicate(compose, folder->prefs->default_to);
3259         }
3260         if (folder->prefs->enable_default_cc) {
3261                 compose_entry_append(compose, folder->prefs->default_cc,
3262                                         COMPOSE_CC, PREF_FOLDER);
3263                 compose_entry_indicate(compose, folder->prefs->default_cc);
3264         }
3265         if (folder->prefs->enable_default_bcc) {
3266                 compose_entry_append(compose, folder->prefs->default_bcc,
3267                                         COMPOSE_BCC, PREF_FOLDER);
3268                 compose_entry_indicate(compose, folder->prefs->default_bcc);
3269         }
3270         if (folder->prefs->enable_default_replyto) {
3271                 compose_entry_append(compose, folder->prefs->default_replyto,
3272                                         COMPOSE_REPLYTO, PREF_FOLDER);
3273                 compose_entry_indicate(compose, folder->prefs->default_replyto);
3274         }
3275 }
3276
3277 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3278 {
3279         gchar *buf, *buf2;
3280         gchar *p;
3281         
3282         if (!compose || !msginfo)
3283                 return;
3284
3285         if (msginfo->subject && *msginfo->subject) {
3286                 buf = p = g_strdup(msginfo->subject);
3287                 p += subject_get_prefix_length(p);
3288                 memmove(buf, p, strlen(p) + 1);
3289
3290                 buf2 = g_strdup_printf("Re: %s", buf);
3291                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3292
3293                 g_free(buf2);
3294                 g_free(buf);
3295         } else
3296                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3297 }
3298
3299 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3300                                     gboolean to_all, gboolean to_ml,
3301                                     gboolean to_sender,
3302                                     gboolean followup_and_reply_to)
3303 {
3304         GSList *cc_list = NULL;
3305         GSList *cur;
3306         gchar *from = NULL;
3307         gchar *replyto = NULL;
3308         gchar *ac_email = NULL;
3309
3310         gboolean reply_to_ml = FALSE;
3311         gboolean default_reply_to = FALSE;
3312
3313         cm_return_if_fail(compose->account != NULL);
3314         cm_return_if_fail(msginfo != NULL);
3315
3316         reply_to_ml = to_ml && compose->ml_post;
3317
3318         default_reply_to = msginfo->folder && 
3319                 msginfo->folder->prefs->enable_default_reply_to;
3320
3321         if (compose->account->protocol != A_NNTP) {
3322                 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3323
3324                 if (reply_to_ml && !default_reply_to) {
3325                         
3326                         gboolean is_subscr = is_subscription(compose->ml_post,
3327                                                              msginfo->from);
3328                         if (!is_subscr) {
3329                                 /* normal answer to ml post with a reply-to */
3330                                 compose_entry_append(compose,
3331                                            compose->ml_post,
3332                                            COMPOSE_TO, PREF_ML);
3333                                 if (compose->replyto)
3334                                         compose_entry_append(compose,
3335                                                 compose->replyto,
3336                                                 COMPOSE_CC, PREF_ML);
3337                         } else {
3338                                 /* answer to subscription confirmation */
3339                                 if (compose->replyto)
3340                                         compose_entry_append(compose,
3341                                                 compose->replyto,
3342                                                 COMPOSE_TO, PREF_ML);
3343                                 else if (msginfo->from)
3344                                         compose_entry_append(compose,
3345                                                 msginfo->from,
3346                                                 COMPOSE_TO, PREF_ML);
3347                         }
3348                 }
3349                 else if (!(to_all || to_sender) && default_reply_to) {
3350                         compose_entry_append(compose,
3351                             msginfo->folder->prefs->default_reply_to,
3352                             COMPOSE_TO, PREF_FOLDER);
3353                         compose_entry_indicate(compose,
3354                                 msginfo->folder->prefs->default_reply_to);
3355                 } else {
3356                         gchar *tmp1 = NULL;
3357                         if (!msginfo->from)
3358                                 return;
3359                         if (to_sender)
3360                                 compose_entry_append(compose, msginfo->from,
3361                                                      COMPOSE_TO, PREF_NONE);
3362                         else if (to_all) {
3363                                 Xstrdup_a(tmp1, msginfo->from, return);
3364                                 extract_address(tmp1);
3365                                 compose_entry_append(compose,
3366                                  (!account_find_from_address(tmp1, FALSE))
3367                                           ? msginfo->from :
3368                                           msginfo->to,
3369                                           COMPOSE_TO, PREF_NONE);
3370                                 if (compose->replyto)
3371                                                 compose_entry_append(compose,
3372                                                         compose->replyto,
3373                                                         COMPOSE_CC, PREF_NONE);
3374                         } else {
3375                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3376                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3377                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3378                                         if (compose->replyto) {
3379                                                 compose_entry_append(compose,
3380                                                         compose->replyto,
3381                                                         COMPOSE_TO, PREF_NONE);
3382                                         } else {
3383                                                 compose_entry_append(compose,
3384                                                           msginfo->from ? msginfo->from : "",
3385                                                           COMPOSE_TO, PREF_NONE);
3386                                         }
3387                                 } else {
3388                                         /* replying to own mail, use original recp */
3389                                         compose_entry_append(compose,
3390                                                   msginfo->to ? msginfo->to : "",
3391                                                   COMPOSE_TO, PREF_NONE);
3392                                         compose_entry_append(compose,
3393                                                   msginfo->cc ? msginfo->cc : "",
3394                                                   COMPOSE_CC, PREF_NONE);
3395                                 }
3396                         }
3397                 }
3398         } else {
3399                 if (to_sender || (compose->followup_to && 
3400                         !strncmp(compose->followup_to, "poster", 6)))
3401                         compose_entry_append
3402                                 (compose, 
3403                                  (compose->replyto ? compose->replyto :
3404                                         msginfo->from ? msginfo->from : ""),
3405                                  COMPOSE_TO, PREF_NONE);
3406                                  
3407                 else if (followup_and_reply_to || to_all) {
3408                         compose_entry_append
3409                                 (compose,
3410                                  (compose->replyto ? compose->replyto :
3411                                  msginfo->from ? msginfo->from : ""),
3412                                  COMPOSE_TO, PREF_NONE);                                
3413                 
3414                         compose_entry_append
3415                                 (compose,
3416                                  compose->followup_to ? compose->followup_to :
3417                                  compose->newsgroups ? compose->newsgroups : "",
3418                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3419
3420                         compose_entry_append
3421                                 (compose,
3422                                  msginfo->cc ? msginfo->cc : "",
3423                                  COMPOSE_CC, PREF_NONE);
3424                 } 
3425                 else 
3426                         compose_entry_append
3427                                 (compose,
3428                                  compose->followup_to ? compose->followup_to :
3429                                  compose->newsgroups ? compose->newsgroups : "",
3430                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3431         }
3432         compose_reply_set_subject(compose, msginfo);
3433
3434         if (to_ml && compose->ml_post) return;
3435         if (!to_all || compose->account->protocol == A_NNTP) return;
3436
3437         if (compose->replyto) {
3438                 Xstrdup_a(replyto, compose->replyto, return);
3439                 extract_address(replyto);
3440         }
3441         if (msginfo->from) {
3442                 Xstrdup_a(from, msginfo->from, return);
3443                 extract_address(from);
3444         }
3445
3446         if (replyto && from)
3447                 cc_list = address_list_append_with_comments(cc_list, from);
3448         if (to_all && msginfo->folder && 
3449             msginfo->folder->prefs->enable_default_reply_to)
3450                 cc_list = address_list_append_with_comments(cc_list,
3451                                 msginfo->folder->prefs->default_reply_to);
3452         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3453         cc_list = address_list_append_with_comments(cc_list, compose->cc);
3454
3455         ac_email = g_utf8_strdown(compose->account->address, -1);
3456
3457         if (cc_list) {
3458                 for (cur = cc_list; cur != NULL; cur = cur->next) {
3459                         gchar *addr = g_utf8_strdown(cur->data, -1);
3460                         extract_address(addr);
3461                 
3462                         if (strcmp(ac_email, addr))
3463                                 compose_entry_append(compose, (gchar *)cur->data,
3464                                                      COMPOSE_CC, PREF_NONE);
3465                         else
3466                                 debug_print("Cc address same as compose account's, ignoring\n");
3467
3468                         g_free(addr);
3469                 }
3470                 
3471                 slist_free_strings_full(cc_list);
3472         }
3473         
3474         g_free(ac_email);
3475 }
3476
3477 #define SET_ENTRY(entry, str) \
3478 { \
3479         if (str && *str) \
3480                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3481 }
3482
3483 #define SET_ADDRESS(type, str) \
3484 { \
3485         if (str && *str) \
3486                 compose_entry_append(compose, str, type, PREF_NONE); \
3487 }
3488
3489 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3490 {
3491         cm_return_if_fail(msginfo != NULL);
3492
3493         SET_ENTRY(subject_entry, msginfo->subject);
3494         SET_ENTRY(from_name, msginfo->from);
3495         SET_ADDRESS(COMPOSE_TO, msginfo->to);
3496         SET_ADDRESS(COMPOSE_CC, compose->cc);
3497         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3498         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3499         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3500         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3501
3502         compose_update_priority_menu_item(compose);
3503         compose_update_privacy_system_menu_item(compose, FALSE);
3504         compose_show_first_last_header(compose, TRUE);
3505 }
3506
3507 #undef SET_ENTRY
3508 #undef SET_ADDRESS
3509
3510 static void compose_insert_sig(Compose *compose, gboolean replace)
3511 {
3512         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3513         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3514         GtkTextMark *mark;
3515         GtkTextIter iter, iter_end;
3516         gint cur_pos, ins_pos;
3517         gboolean prev_autowrap;
3518         gboolean found = FALSE;
3519         gboolean exists = FALSE;
3520         
3521         cm_return_if_fail(compose->account != NULL);
3522
3523         BLOCK_WRAP();
3524
3525         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3526                                         G_CALLBACK(compose_changed_cb),
3527                                         compose);
3528         
3529         mark = gtk_text_buffer_get_insert(buffer);
3530         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3531         cur_pos = gtk_text_iter_get_offset (&iter);
3532         ins_pos = cur_pos;
3533
3534         gtk_text_buffer_get_end_iter(buffer, &iter);
3535
3536         exists = (compose->sig_str != NULL);
3537
3538         if (replace) {
3539                 GtkTextIter first_iter, start_iter, end_iter;
3540
3541                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3542
3543                 if (!exists || compose->sig_str[0] == '\0')
3544                         found = FALSE;
3545                 else
3546                         found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3547                                         compose->signature_tag);
3548
3549                 if (found) {
3550                         /* include previous \n\n */
3551                         gtk_text_iter_backward_chars(&first_iter, 1);
3552                         start_iter = first_iter;
3553                         end_iter = first_iter;
3554                         /* skip re-start */
3555                         found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3556                                         compose->signature_tag);
3557                         found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3558                                         compose->signature_tag);
3559                         if (found) {
3560                                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3561                                 iter = start_iter;
3562                         }
3563                 } 
3564         } 
3565
3566         g_free(compose->sig_str);
3567         compose->sig_str = account_get_signature_str(compose->account);
3568
3569         cur_pos = gtk_text_iter_get_offset(&iter);
3570
3571         if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3572                 g_free(compose->sig_str);
3573                 compose->sig_str = NULL;
3574         } else {
3575                 if (compose->sig_inserted == FALSE)
3576                         gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3577                 compose->sig_inserted = TRUE;
3578
3579                 cur_pos = gtk_text_iter_get_offset(&iter);
3580                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3581                 /* remove \n\n */
3582                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3583                 gtk_text_iter_forward_chars(&iter, 1);
3584                 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3585                 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3586
3587                 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3588                         cur_pos = gtk_text_buffer_get_char_count (buffer);
3589         }
3590
3591         /* put the cursor where it should be 
3592          * either where the quote_fmt says, either where it was */
3593         if (compose->set_cursor_pos < 0)
3594                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3595         else
3596                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, 
3597                         compose->set_cursor_pos);
3598         
3599         compose->set_cursor_pos = -1;
3600         gtk_text_buffer_place_cursor(buffer, &iter);
3601         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3602                                         G_CALLBACK(compose_changed_cb),
3603                                         compose);
3604                 
3605         UNBLOCK_WRAP();
3606 }
3607
3608 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3609 {
3610         GtkTextView *text;
3611         GtkTextBuffer *buffer;
3612         GtkTextMark *mark;
3613         GtkTextIter iter;
3614         const gchar *cur_encoding;
3615         gchar buf[BUFFSIZE];
3616         gint len;
3617         FILE *fp;
3618         gboolean prev_autowrap;
3619         GStatBuf file_stat;
3620         int ret;
3621         GString *file_contents = NULL;
3622         ComposeInsertResult result = COMPOSE_INSERT_SUCCESS;
3623
3624         cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3625
3626         /* get the size of the file we are about to insert */
3627         ret = g_stat(file, &file_stat);
3628         if (ret != 0) {
3629                 gchar *shortfile = g_path_get_basename(file);
3630                 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3631                 g_free(shortfile);
3632                 return COMPOSE_INSERT_NO_FILE;
3633         } else if (prefs_common.warn_large_insert == TRUE) {
3634
3635                 /* ask user for confirmation if the file is large */
3636                 if (prefs_common.warn_large_insert_size < 0 ||
3637                     file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3638                         AlertValue aval;
3639                         gchar *msg;
3640
3641                         msg = g_strdup_printf(_("You are about to insert a file of %s "
3642                                                 "in the message body. Are you sure you want to do that?"),
3643                                                 to_human_readable(file_stat.st_size));
3644                         aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3645                                         g_strconcat("+", _("_Insert"), NULL), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3646                         g_free(msg);
3647
3648                         /* do we ask for confirmation next time? */
3649                         if (aval & G_ALERTDISABLE) {
3650                                 /* no confirmation next time, disable feature in preferences */
3651                                 aval &= ~G_ALERTDISABLE;
3652                                 prefs_common.warn_large_insert = FALSE;
3653                         }
3654
3655                         /* abort file insertion if user canceled action */
3656                         if (aval != G_ALERTALTERNATE) {
3657                                 return COMPOSE_INSERT_NO_FILE;
3658                         }
3659                 }
3660         }
3661
3662
3663         if ((fp = g_fopen(file, "rb")) == NULL) {
3664                 FILE_OP_ERROR(file, "fopen");
3665                 return COMPOSE_INSERT_READ_ERROR;
3666         }
3667
3668         prev_autowrap = compose->autowrap;
3669         compose->autowrap = FALSE;
3670
3671         text = GTK_TEXT_VIEW(compose->text);
3672         buffer = gtk_text_view_get_buffer(text);
3673         mark = gtk_text_buffer_get_insert(buffer);
3674         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3675
3676         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3677                                         G_CALLBACK(text_inserted),
3678                                         compose);
3679
3680         cur_encoding = conv_get_locale_charset_str_no_utf8();
3681
3682         file_contents = g_string_new("");
3683         while (fgets(buf, sizeof(buf), fp) != NULL) {
3684                 gchar *str;
3685
3686                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3687                         str = g_strdup(buf);
3688                 else {
3689                         codeconv_set_strict(TRUE);
3690                         str = conv_codeset_strdup
3691                                 (buf, cur_encoding, CS_INTERNAL);
3692                         codeconv_set_strict(FALSE);
3693
3694                         if (!str) {
3695                                 result = COMPOSE_INSERT_INVALID_CHARACTER;
3696                                 break;
3697                         }
3698                 }
3699                 if (!str) continue;
3700
3701                 /* strip <CR> if DOS/Windows file,
3702                    replace <CR> with <LF> if Macintosh file. */
3703                 strcrchomp(str);
3704                 len = strlen(str);
3705                 if (len > 0 && str[len - 1] != '\n') {
3706                         while (--len >= 0)
3707                                 if (str[len] == '\r') str[len] = '\n';
3708                 }
3709
3710                 file_contents = g_string_append(file_contents, str);
3711                 g_free(str);
3712         }
3713
3714         if (result == COMPOSE_INSERT_SUCCESS) {
3715                 gtk_text_buffer_insert(buffer, &iter, file_contents->str, -1);
3716
3717                 compose_changed_cb(NULL, compose);
3718                 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3719                                                   G_CALLBACK(text_inserted),
3720                                                   compose);
3721                 compose->autowrap = prev_autowrap;
3722                 if (compose->autowrap)
3723                         compose_wrap_all(compose);
3724         }
3725
3726         g_string_free(file_contents, TRUE);
3727         fclose(fp);
3728
3729         return result;
3730 }
3731
3732 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3733                                   const gchar *filename,
3734                                   const gchar *content_type,
3735                                   const gchar *charset)
3736 {
3737         AttachInfo *ainfo;
3738         GtkTreeIter iter;
3739         FILE *fp;
3740         off_t size;
3741         GAuto *auto_ainfo;
3742         gchar *size_text;
3743         GtkListStore *store;
3744         gchar *name;
3745         gboolean has_binary = FALSE;
3746
3747         if (!is_file_exist(file)) {
3748                 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3749                 gboolean result = FALSE;
3750                 if (file_from_uri && is_file_exist(file_from_uri)) {
3751                         result = compose_attach_append(
3752                                                 compose, file_from_uri,
3753                                                 filename, content_type,
3754                                                 charset);
3755                 }
3756                 g_free(file_from_uri);
3757                 if (result)
3758                         return TRUE;
3759                 alertpanel_error("File %s doesn't exist or permission denied\n", filename);
3760                 return FALSE;
3761         }
3762         if ((size = get_file_size(file)) < 0) {
3763                 alertpanel_error("Can't get file size of %s\n", filename);
3764                 return FALSE;
3765         }
3766
3767         /* In batch mode, we allow 0-length files to be attached no questions asked */
3768         if (size == 0 && !compose->batch) {
3769                 gchar * msg = g_strdup_printf(_("File %s is empty."), filename);
3770                 AlertValue aval = alertpanel_full(_("Empty file"), msg, 
3771                                 GTK_STOCK_CANCEL,  g_strconcat("+", _("_Attach anyway"), NULL), NULL, FALSE,
3772                                 NULL, ALERT_WARNING, G_ALERTDEFAULT);
3773                 g_free(msg);
3774
3775                 if (aval != G_ALERTALTERNATE) {
3776                         return FALSE;
3777                 }
3778         }
3779         if ((fp = g_fopen(file, "rb")) == NULL) {
3780                 alertpanel_error(_("Can't read %s."), filename);
3781                 return FALSE;
3782         }
3783         fclose(fp);
3784
3785         ainfo = g_new0(AttachInfo, 1);
3786         auto_ainfo = g_auto_pointer_new_with_free
3787                         (ainfo, (GFreeFunc) compose_attach_info_free); 
3788         ainfo->file = g_strdup(file);
3789
3790         if (content_type) {
3791                 ainfo->content_type = g_strdup(content_type);
3792                 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3793                         MsgInfo *msginfo;
3794                         MsgFlags flags = {0, 0};
3795
3796                         if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3797                                 ainfo->encoding = ENC_7BIT;
3798                         else
3799                                 ainfo->encoding = ENC_8BIT;
3800
3801                         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3802                         if (msginfo && msginfo->subject)
3803                                 name = g_strdup(msginfo->subject);
3804                         else
3805                                 name = g_path_get_basename(filename ? filename : file);
3806
3807                         ainfo->name = g_strdup_printf(_("Message: %s"), name);
3808
3809                         procmsg_msginfo_free(&msginfo);
3810                 } else {
3811                         if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3812                                 ainfo->charset = g_strdup(charset);
3813                                 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3814                         } else {
3815                                 ainfo->encoding = ENC_BASE64;
3816                         }
3817                         name = g_path_get_basename(filename ? filename : file);
3818                         ainfo->name = g_strdup(name);
3819                 }
3820                 g_free(name);
3821         } else {
3822                 ainfo->content_type = procmime_get_mime_type(file);
3823                 if (!ainfo->content_type) {
3824                         ainfo->content_type =
3825                                 g_strdup("application/octet-stream");
3826                         ainfo->encoding = ENC_BASE64;
3827                 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3828                         ainfo->encoding =
3829                                 procmime_get_encoding_for_text_file(file, &has_binary);
3830                 else
3831                         ainfo->encoding = ENC_BASE64;
3832                 name = g_path_get_basename(filename ? filename : file);
3833                 ainfo->name = g_strdup(name);   
3834                 g_free(name);
3835         }
3836
3837         if (ainfo->name != NULL
3838         &&  !strcmp(ainfo->name, ".")) {
3839                 g_free(ainfo->name);
3840                 ainfo->name = NULL;
3841         }
3842
3843         if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3844                 g_free(ainfo->content_type);
3845                 ainfo->content_type = g_strdup("application/octet-stream");
3846                 g_free(ainfo->charset);
3847                 ainfo->charset = NULL;
3848         }
3849
3850         ainfo->size = (goffset)size;
3851         size_text = to_human_readable((goffset)size);
3852
3853         store = GTK_LIST_STORE(gtk_tree_view_get_model
3854                         (GTK_TREE_VIEW(compose->attach_clist)));
3855                 
3856         gtk_list_store_append(store, &iter);
3857         gtk_list_store_set(store, &iter, 
3858                            COL_MIMETYPE, ainfo->content_type,
3859                            COL_SIZE, size_text,
3860                            COL_NAME, ainfo->name,
3861                            COL_CHARSET, ainfo->charset,
3862                            COL_DATA, ainfo,
3863                            COL_AUTODATA, auto_ainfo,
3864                            -1);
3865         
3866         g_auto_pointer_free(auto_ainfo);
3867         compose_attach_update_label(compose);
3868         return TRUE;
3869 }
3870
3871 static void compose_use_signing(Compose *compose, gboolean use_signing)
3872 {
3873         compose->use_signing = use_signing;
3874         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3875 }
3876
3877 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3878 {
3879         compose->use_encryption = use_encryption;
3880         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3881 }
3882
3883 #define NEXT_PART_NOT_CHILD(info)  \
3884 {  \
3885         node = info->node;  \
3886         while (node->children)  \
3887                 node = g_node_last_child(node);  \
3888         info = procmime_mimeinfo_next((MimeInfo *)node->data);  \
3889 }
3890
3891 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3892 {
3893         MimeInfo *mimeinfo;
3894         MimeInfo *child;
3895         MimeInfo *firsttext = NULL;
3896         MimeInfo *encrypted = NULL;
3897         GNode    *node;
3898         gchar *outfile;
3899         const gchar *partname = NULL;
3900
3901         mimeinfo = procmime_scan_message(msginfo);
3902         if (!mimeinfo) return;
3903
3904         if (mimeinfo->node->children == NULL) {
3905                 procmime_mimeinfo_free_all(&mimeinfo);
3906                 return;
3907         }
3908
3909         /* find first content part */
3910         child = (MimeInfo *) mimeinfo->node->children->data;
3911         while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3912                 child = (MimeInfo *)child->node->children->data;
3913
3914         if (child) {
3915                 if (child->type == MIMETYPE_TEXT) {
3916                         firsttext = child;
3917                         debug_print("First text part found\n");
3918                 } else if (compose->mode == COMPOSE_REEDIT &&
3919                          child->type == MIMETYPE_APPLICATION &&
3920                          !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3921                         encrypted = (MimeInfo *)child->node->parent->data;
3922                 }
3923         }
3924         child = (MimeInfo *) mimeinfo->node->children->data;
3925         while (child != NULL) {
3926                 gint err;
3927
3928                 if (child == encrypted) {
3929                         /* skip this part of tree */
3930                         NEXT_PART_NOT_CHILD(child);
3931                         continue;
3932                 }
3933
3934                 if (child->type == MIMETYPE_MULTIPART) {
3935                         /* get the actual content */
3936                         child = procmime_mimeinfo_next(child);
3937                         continue;
3938                 }
3939                     
3940                 if (child == firsttext) {
3941                         child = procmime_mimeinfo_next(child);
3942                         continue;
3943                 }
3944
3945                 outfile = procmime_get_tmp_file_name(child);
3946                 if ((err = procmime_get_part(outfile, child)) < 0)
3947                         g_warning("Can't get the part of multipart message. (%s)", g_strerror(-err));
3948                 else {
3949                         gchar *content_type;
3950
3951                         content_type = procmime_get_content_type_str(child->type, child->subtype);
3952
3953                         /* if we meet a pgp signature, we don't attach it, but
3954                          * we force signing. */
3955                         if ((strcmp(content_type, "application/pgp-signature") &&
3956                             strcmp(content_type, "application/pkcs7-signature") &&
3957                             strcmp(content_type, "application/x-pkcs7-signature"))
3958                             || compose->mode == COMPOSE_REDIRECT) {
3959                                 partname = procmime_mimeinfo_get_parameter(child, "filename");
3960                                 if (partname == NULL)
3961                                         partname = procmime_mimeinfo_get_parameter(child, "name");
3962                                 if (partname == NULL)
3963                                         partname = "";
3964                                 compose_attach_append(compose, outfile, 
3965                                                       partname, content_type,
3966                                                       procmime_mimeinfo_get_parameter(child, "charset"));
3967                         } else {
3968                                 compose_force_signing(compose, compose->account, NULL);
3969                         }
3970                         g_free(content_type);
3971                 }
3972                 g_free(outfile);
3973                 NEXT_PART_NOT_CHILD(child);
3974         }
3975         procmime_mimeinfo_free_all(&mimeinfo);
3976 }
3977
3978 #undef NEXT_PART_NOT_CHILD
3979
3980
3981
3982 typedef enum {
3983         WAIT_FOR_INDENT_CHAR,
3984         WAIT_FOR_INDENT_CHAR_OR_SPACE,
3985 } IndentState;
3986
3987 /* return indent length, we allow:
3988    indent characters followed by indent characters or spaces/tabs,
3989    alphabets and numbers immediately followed by indent characters,
3990    and the repeating sequences of the above
3991    If quote ends with multiple spaces, only the first one is included. */
3992 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3993                                     const GtkTextIter *start, gint *len)
3994 {
3995         GtkTextIter iter = *start;
3996         gunichar wc;
3997         gchar ch[6];
3998         gint clen;
3999         IndentState state = WAIT_FOR_INDENT_CHAR;
4000         gboolean is_space;
4001         gboolean is_indent;
4002         gint alnum_count = 0;
4003         gint space_count = 0;
4004         gint quote_len = 0;
4005
4006         if (prefs_common.quote_chars == NULL) {
4007                 return 0 ;
4008         }
4009
4010         while (!gtk_text_iter_ends_line(&iter)) {
4011                 wc = gtk_text_iter_get_char(&iter);
4012                 if (g_unichar_iswide(wc))
4013                         break;
4014                 clen = g_unichar_to_utf8(wc, ch);
4015                 if (clen != 1)
4016                         break;
4017
4018                 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
4019                 is_space = g_unichar_isspace(wc);
4020
4021                 if (state == WAIT_FOR_INDENT_CHAR) {
4022                         if (!is_indent && !g_unichar_isalnum(wc))
4023                                 break;
4024                         if (is_indent) {
4025                                 quote_len += alnum_count + space_count + 1;
4026                                 alnum_count = space_count = 0;
4027                                 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
4028                         } else
4029                                 alnum_count++;
4030                 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
4031                         if (!is_indent && !is_space && !g_unichar_isalnum(wc))
4032                                 break;
4033                         if (is_space)
4034                                 space_count++;
4035                         else if (is_indent) {
4036                                 quote_len += alnum_count + space_count + 1;
4037                                 alnum_count = space_count = 0;
4038                         } else {
4039                                 alnum_count++;
4040                                 state = WAIT_FOR_INDENT_CHAR;
4041                         }
4042                 }
4043
4044                 gtk_text_iter_forward_char(&iter);
4045         }
4046
4047         if (quote_len > 0 && space_count > 0)
4048                 quote_len++;
4049
4050         if (len)
4051                 *len = quote_len;
4052
4053         if (quote_len > 0) {
4054                 iter = *start;
4055                 gtk_text_iter_forward_chars(&iter, quote_len);
4056                 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
4057         }
4058
4059         return NULL;
4060 }
4061
4062 /* return >0 if the line is itemized */
4063 static int compose_itemized_length(GtkTextBuffer *buffer,
4064                                     const GtkTextIter *start)
4065 {
4066         GtkTextIter iter = *start;
4067         gunichar wc;
4068         gchar ch[6];
4069         gint clen;
4070         gint len = 0;
4071         if (gtk_text_iter_ends_line(&iter))
4072                 return 0;
4073
4074         while (1) {
4075                 len++;
4076                 wc = gtk_text_iter_get_char(&iter);
4077                 if (!g_unichar_isspace(wc))
4078                         break;
4079                 gtk_text_iter_forward_char(&iter);
4080                 if (gtk_text_iter_ends_line(&iter))
4081                         return 0;
4082         }
4083
4084         clen = g_unichar_to_utf8(wc, ch);
4085         if (clen != 1)
4086                 return 0;
4087
4088         if (!strchr("*-+", ch[0]))
4089                 return 0;
4090
4091         gtk_text_iter_forward_char(&iter);
4092         if (gtk_text_iter_ends_line(&iter))
4093                 return 0;
4094         wc = gtk_text_iter_get_char(&iter);
4095         if (g_unichar_isspace(wc)) {
4096                 return len+1;
4097         }
4098         return 0;
4099 }
4100
4101 /* return the string at the start of the itemization */
4102 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
4103                                     const GtkTextIter *start)
4104 {
4105         GtkTextIter iter = *start;
4106         gunichar wc;
4107         gint len = 0;
4108         GString *item_chars = g_string_new("");
4109         gchar *str = NULL;
4110
4111         if (gtk_text_iter_ends_line(&iter))
4112                 return NULL;
4113
4114         while (1) {
4115                 len++;
4116                 wc = gtk_text_iter_get_char(&iter);
4117                 if (!g_unichar_isspace(wc))
4118                         break;
4119                 gtk_text_iter_forward_char(&iter);
4120                 if (gtk_text_iter_ends_line(&iter))
4121                         break;
4122                 g_string_append_unichar(item_chars, wc);
4123         }
4124
4125         str = item_chars->str;
4126         g_string_free(item_chars, FALSE);
4127         return str;
4128 }
4129
4130 /* return the number of spaces at a line's start */
4131 static int compose_left_offset_length(GtkTextBuffer *buffer,
4132                                     const GtkTextIter *start)
4133 {
4134         GtkTextIter iter = *start;
4135         gunichar wc;
4136         gint len = 0;
4137         if (gtk_text_iter_ends_line(&iter))
4138                 return 0;
4139
4140         while (1) {
4141                 wc = gtk_text_iter_get_char(&iter);
4142                 if (!g_unichar_isspace(wc))
4143                         break;
4144                 len++;
4145                 gtk_text_iter_forward_char(&iter);
4146                 if (gtk_text_iter_ends_line(&iter))
4147                         return 0;
4148         }
4149
4150         gtk_text_iter_forward_char(&iter);
4151         if (gtk_text_iter_ends_line(&iter))
4152                 return 0;
4153         return len;
4154 }
4155
4156 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
4157                                            const GtkTextIter *start,
4158                                            GtkTextIter *break_pos,
4159                                            gint max_col,
4160                                            gint quote_len)
4161 {
4162         GtkTextIter iter = *start, line_end = *start;
4163         PangoLogAttr *attrs;
4164         gchar *str;
4165         gchar *p;
4166         gint len;
4167         gint i;
4168         gint col = 0;
4169         gint pos = 0;
4170         gboolean can_break = FALSE;
4171         gboolean do_break = FALSE;
4172         gboolean was_white = FALSE;
4173         gboolean prev_dont_break = FALSE;
4174
4175         gtk_text_iter_forward_to_line_end(&line_end);
4176         str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
4177         len = g_utf8_strlen(str, -1);
4178         
4179         if (len == 0) {
4180                 g_free(str);
4181                 g_warning("compose_get_line_break_pos: len = 0!");
4182                 return FALSE;
4183         }
4184
4185         /* g_print("breaking line: %d: %s (len = %d)\n",
4186                 gtk_text_iter_get_line(&iter), str, len); */
4187
4188         attrs = g_new(PangoLogAttr, len + 1);
4189
4190         pango_default_break(str, -1, NULL, attrs, len + 1);
4191
4192         p = str;
4193
4194         /* skip quote and leading spaces */
4195         for (i = 0; *p != '\0' && i < len; i++) {
4196                 gunichar wc;
4197
4198                 wc = g_utf8_get_char(p);
4199                 if (i >= quote_len && !g_unichar_isspace(wc))
4200                         break;
4201                 if (g_unichar_iswide(wc))
4202                         col += 2;
4203                 else if (*p == '\t')
4204                         col += 8;
4205                 else
4206                         col++;
4207                 p = g_utf8_next_char(p);
4208         }
4209
4210         for (; *p != '\0' && i < len; i++) {
4211                 PangoLogAttr *attr = attrs + i;
4212                 gunichar wc;
4213                 gint uri_len;
4214
4215                 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
4216                         pos = i;
4217                 
4218                 was_white = attr->is_white;
4219
4220                 /* don't wrap URI */
4221                 if ((uri_len = get_uri_len(p)) > 0) {
4222                         col += uri_len;
4223                         if (pos > 0 && col > max_col) {
4224                                 do_break = TRUE;
4225                                 break;
4226                         }
4227                         i += uri_len - 1;
4228                         p += uri_len;
4229                         can_break = TRUE;
4230                         continue;
4231                 }
4232
4233                 wc = g_utf8_get_char(p);
4234                 if (g_unichar_iswide(wc)) {
4235                         col += 2;
4236                         if (prev_dont_break && can_break && attr->is_line_break)
4237                                 pos = i;
4238                 } else if (*p == '\t')
4239                         col += 8;
4240                 else
4241                         col++;
4242                 if (pos > 0 && col > max_col) {
4243                         do_break = TRUE;
4244                         break;
4245                 }
4246
4247                 if (*p == '-' || *p == '/')
4248                         prev_dont_break = TRUE;
4249                 else
4250                         prev_dont_break = FALSE;
4251
4252                 p = g_utf8_next_char(p);
4253                 can_break = TRUE;
4254         }
4255
4256 /*      debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col); */
4257
4258         g_free(attrs);
4259         g_free(str);
4260
4261         *break_pos = *start;
4262         gtk_text_iter_set_line_offset(break_pos, pos);
4263
4264         return do_break;
4265 }
4266
4267 static gboolean compose_join_next_line(Compose *compose,
4268                                        GtkTextBuffer *buffer,
4269                                        GtkTextIter *iter,
4270                                        const gchar *quote_str)
4271 {
4272         GtkTextIter iter_ = *iter, cur, prev, next, end;
4273         PangoLogAttr attrs[3];
4274         gchar *str;
4275         gchar *next_quote_str;
4276         gunichar wc1, wc2;
4277         gint quote_len;
4278         gboolean keep_cursor = FALSE;
4279
4280         if (!gtk_text_iter_forward_line(&iter_) ||
4281             gtk_text_iter_ends_line(&iter_)) {
4282                 return FALSE;
4283         }
4284         next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
4285
4286         if ((quote_str || next_quote_str) &&
4287             strcmp2(quote_str, next_quote_str) != 0) {
4288                 g_free(next_quote_str);
4289                 return FALSE;
4290         }
4291         g_free(next_quote_str);
4292
4293         end = iter_;
4294         if (quote_len > 0) {
4295                 gtk_text_iter_forward_chars(&end, quote_len);
4296                 if (gtk_text_iter_ends_line(&end)) {
4297                         return FALSE;
4298                 }
4299         }
4300
4301         /* don't join itemized lines */
4302         if (compose_itemized_length(buffer, &end) > 0) {
4303                 return FALSE;
4304         }
4305
4306         /* don't join signature separator */
4307         if (compose_is_sig_separator(compose, buffer, &iter_)) {
4308                 return FALSE;
4309         }
4310         /* delete quote str */
4311         if (quote_len > 0)
4312                 gtk_text_buffer_delete(buffer, &iter_, &end);
4313
4314         /* don't join line breaks put by the user */
4315         prev = cur = iter_;
4316         gtk_text_iter_backward_char(&cur);
4317         if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
4318                 gtk_text_iter_forward_char(&cur);
4319                 *iter = cur;
4320                 return FALSE;
4321         }
4322         gtk_text_iter_forward_char(&cur);
4323         /* delete linebreak and extra spaces */
4324         while (gtk_text_iter_backward_char(&cur)) {
4325                 wc1 = gtk_text_iter_get_char(&cur);
4326                 if (!g_unichar_isspace(wc1))
4327                         break;
4328                 prev = cur;
4329         }
4330         next = cur = iter_;
4331         while (!gtk_text_iter_ends_line(&cur)) {
4332                 wc1 = gtk_text_iter_get_char(&cur);
4333                 if (!g_unichar_isspace(wc1))
4334                         break;
4335                 gtk_text_iter_forward_char(&cur);
4336                 next = cur;
4337         }
4338         if (!gtk_text_iter_equal(&prev, &next)) {
4339                 GtkTextMark *mark;
4340
4341                 mark = gtk_text_buffer_get_insert(buffer);
4342                 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
4343                 if (gtk_text_iter_equal(&prev, &cur))
4344                         keep_cursor = TRUE;
4345                 gtk_text_buffer_delete(buffer, &prev, &next);
4346         }
4347         iter_ = prev;
4348
4349         /* insert space if required */
4350         gtk_text_iter_backward_char(&prev);
4351         wc1 = gtk_text_iter_get_char(&prev);
4352         wc2 = gtk_text_iter_get_char(&next);
4353         gtk_text_iter_forward_char(&next);
4354         str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
4355         pango_default_break(str, -1, NULL, attrs, 3);
4356         if (!attrs[1].is_line_break ||
4357             (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
4358                 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
4359                 if (keep_cursor) {
4360                         gtk_text_iter_backward_char(&iter_);
4361                         gtk_text_buffer_place_cursor(buffer, &iter_);
4362                 }
4363         }
4364         g_free(str);
4365
4366         *iter = iter_;
4367         return TRUE;
4368 }
4369
4370 #define ADD_TXT_POS(bp_, ep_, pti_) \
4371         if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
4372                 last = last->next; \
4373                 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
4374                 last->next = NULL; \
4375         } else { \
4376                 g_warning("alloc error scanning URIs"); \
4377         }
4378
4379 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
4380 {
4381         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4382         GtkTextBuffer *buffer;
4383         GtkTextIter iter, break_pos, end_of_line;
4384         gchar *quote_str = NULL;
4385         gint quote_len;
4386         gboolean wrap_quote = force || prefs_common.linewrap_quote;
4387         gboolean prev_autowrap = compose->autowrap;
4388         gint startq_offset = -1, noq_offset = -1;
4389         gint uri_start = -1, uri_stop = -1;
4390         gint nouri_start = -1, nouri_stop = -1;
4391         gint num_blocks = 0;
4392         gint quotelevel = -1;
4393         gboolean modified = force;
4394         gboolean removed = FALSE;
4395         gboolean modified_before_remove = FALSE;
4396         gint lines = 0;
4397         gboolean start = TRUE;
4398         gint itemized_len = 0, rem_item_len = 0;
4399         gchar *itemized_chars = NULL;
4400         gboolean item_continuation = FALSE;
4401
4402         if (force) {
4403                 modified = TRUE;
4404         }
4405         if (compose->draft_timeout_tag == COMPOSE_DRAFT_TIMEOUT_FORBIDDEN) {
4406                 modified = TRUE;
4407         }
4408
4409         compose->autowrap = FALSE;
4410
4411         buffer = gtk_text_view_get_buffer(text);
4412         undo_wrapping(compose->undostruct, TRUE);
4413         if (par_iter) {
4414                 iter = *par_iter;
4415         } else {
4416                 GtkTextMark *mark;
4417                 mark = gtk_text_buffer_get_insert(buffer);
4418                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4419         }
4420
4421
4422         if (compose->draft_timeout_tag == COMPOSE_DRAFT_TIMEOUT_FORBIDDEN) {
4423                 if (gtk_text_iter_ends_line(&iter)) {
4424                         while (gtk_text_iter_ends_line(&iter) &&
4425                                gtk_text_iter_forward_line(&iter))
4426                                 ;
4427                 } else {
4428                         while (gtk_text_iter_backward_line(&iter)) {
4429                                 if (gtk_text_iter_ends_line(&iter)) {
4430                                         gtk_text_iter_forward_line(&iter);
4431                                         break;
4432                                 }
4433                         }
4434                 }
4435         } else {
4436                 /* move to line start */
4437                 gtk_text_iter_set_line_offset(&iter, 0);
4438         }
4439         
4440         itemized_len = compose_itemized_length(buffer, &iter);
4441         
4442         if (!itemized_len) {
4443                 itemized_len = compose_left_offset_length(buffer, &iter);
4444                 item_continuation = TRUE;
4445         }
4446
4447         if (itemized_len)
4448                 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4449
4450         /* go until paragraph end (empty line) */
4451         while (start || !gtk_text_iter_ends_line(&iter)) {
4452                 gchar *scanpos = NULL;
4453                 /* parse table - in order of priority */
4454                 struct table {
4455                         const gchar *needle; /* token */
4456
4457                         /* token search function */
4458                         gchar    *(*search)     (const gchar *haystack,
4459                                                  const gchar *needle);
4460                         /* part parsing function */
4461                         gboolean  (*parse)      (const gchar *start,
4462                                                  const gchar *scanpos,
4463                                                  const gchar **bp_,
4464                                                  const gchar **ep_,
4465                                                  gboolean hdr);
4466                         /* part to URI function */
4467                         gchar    *(*build_uri)  (const gchar *bp,
4468                                                  const gchar *ep);
4469                 };
4470
4471                 static struct table parser[] = {
4472                         {"http://",  strcasestr, get_uri_part,   make_uri_string},
4473                         {"https://", strcasestr, get_uri_part,   make_uri_string},
4474                         {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
4475                         {"sftp://",  strcasestr, get_uri_part,   make_uri_string},
4476                         {"gopher://",strcasestr, get_uri_part,   make_uri_string},
4477                         {"www.",     strcasestr, get_uri_part,   make_http_string},
4478                         {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
4479                         {"@",        strcasestr, get_email_part, make_email_string}
4480                 };
4481                 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4482                 gint last_index = PARSE_ELEMS;
4483                 gint  n;
4484                 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4485                 gint walk_pos;
4486                 
4487                 start = FALSE;
4488                 if (!prev_autowrap && num_blocks == 0) {
4489                         num_blocks++;
4490                         g_signal_handlers_block_by_func(G_OBJECT(buffer),
4491                                         G_CALLBACK(text_inserted),
4492                                         compose);
4493                 }
4494                 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4495                         goto colorize;
4496
4497                 uri_start = uri_stop = -1;
4498                 quote_len = 0;
4499                 quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
4500
4501                 if (quote_str) {
4502 /*                      debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str); */
4503                         if (startq_offset == -1) 
4504                                 startq_offset = gtk_text_iter_get_offset(&iter);
4505                         quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4506                         if (quotelevel > 2) {
4507                                 /* recycle colors */
4508                                 if (prefs_common.recycle_quote_colors)
4509                                         quotelevel %= 3;
4510                                 else
4511                                         quotelevel = 2;
4512                         }
4513                         if (!wrap_quote) {
4514                                 goto colorize;
4515                         }
4516                 } else {
4517                         if (startq_offset == -1)
4518                                 noq_offset = gtk_text_iter_get_offset(&iter);
4519                         quotelevel = -1;
4520                 }
4521
4522                 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4523                         goto colorize;
4524                 }
4525                 if (gtk_text_iter_ends_line(&iter)) {
4526                         goto colorize;
4527                 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4528                                                prefs_common.linewrap_len,
4529                                                quote_len)) {
4530                         GtkTextIter prev, next, cur;
4531                         if (prev_autowrap != FALSE || force) {
4532                                 compose->automatic_break = TRUE;
4533                                 modified = TRUE;
4534                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4535                                 compose->automatic_break = FALSE;
4536                                 if (itemized_len && compose->autoindent) {
4537                                         gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4538                                         if (!item_continuation)
4539                                                 gtk_text_buffer_insert(buffer, &break_pos, "  ", 2);
4540                                 }
4541                         } else if (quote_str && wrap_quote) {
4542                                 compose->automatic_break = TRUE;
4543                                 modified = TRUE;
4544                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4545                                 compose->automatic_break = FALSE;
4546                                 if (itemized_len && compose->autoindent) {
4547                                         gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4548                                         if (!item_continuation)
4549                                                 gtk_text_buffer_insert(buffer, &break_pos, "  ", 2);
4550                                 }
4551                         } else 
4552                                 goto colorize;
4553                         /* remove trailing spaces */
4554                         cur = break_pos;
4555                         rem_item_len = itemized_len;
4556                         while (compose->autoindent && rem_item_len-- > 0)
4557                                 gtk_text_iter_backward_char(&cur);
4558                         gtk_text_iter_backward_char(&cur);
4559
4560                         prev = next = cur;
4561                         while (!gtk_text_iter_starts_line(&cur)) {
4562                                 gunichar wc;
4563
4564                                 gtk_text_iter_backward_char(&cur);
4565                                 wc = gtk_text_iter_get_char(&cur);
4566                                 if (!g_unichar_isspace(wc))
4567                                         break;
4568                                 prev = cur;
4569                         }
4570                         if (!gtk_text_iter_equal(&prev, &next)) {
4571                                 gtk_text_buffer_delete(buffer, &prev, &next);
4572                                 break_pos = next;
4573                                 gtk_text_iter_forward_char(&break_pos);
4574                         }
4575
4576                         if (quote_str)
4577                                 gtk_text_buffer_insert(buffer, &break_pos,
4578                                                        quote_str, -1);
4579
4580                         iter = break_pos;
4581                         modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4582
4583                         /* move iter to current line start */
4584                         gtk_text_iter_set_line_offset(&iter, 0);
4585                         if (quote_str) {
4586                                 g_free(quote_str);
4587                                 quote_str = NULL;
4588                         }
4589                         continue;       
4590                 } else {
4591                         /* move iter to next line start */
4592                         iter = break_pos;
4593                         lines++;
4594                 }
4595
4596 colorize:
4597                 if (!prev_autowrap && num_blocks > 0) {
4598                         num_blocks--;
4599                         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4600                                         G_CALLBACK(text_inserted),
4601                                         compose);
4602                 }
4603                 end_of_line = iter;
4604                 while (!gtk_text_iter_ends_line(&end_of_line)) {
4605                         gtk_text_iter_forward_char(&end_of_line);
4606                 }
4607                 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4608
4609                 nouri_start = gtk_text_iter_get_offset(&iter);
4610                 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4611
4612                 walk_pos = gtk_text_iter_get_offset(&iter);
4613                 /* FIXME: this looks phony. scanning for anything in the parse table */
4614                 for (n = 0; n < PARSE_ELEMS; n++) {
4615                         gchar *tmp;
4616
4617                         tmp = parser[n].search(walk, parser[n].needle);
4618                         if (tmp) {
4619                                 if (scanpos == NULL || tmp < scanpos) {
4620                                         scanpos = tmp;
4621                                         last_index = n;
4622                                 }
4623                         }                                       
4624                 }
4625
4626                 bp = ep = 0;
4627                 if (scanpos) {
4628                         /* check if URI can be parsed */
4629                         if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4630                                         (const gchar **)&ep, FALSE)
4631                             && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4632                                         walk = ep;
4633                         } else
4634                                 walk = scanpos +
4635                                         strlen(parser[last_index].needle);
4636                 } 
4637                 if (bp && ep) {
4638                         uri_start = walk_pos + (bp - o_walk);
4639                         uri_stop  = walk_pos + (ep - o_walk);
4640                 }
4641                 g_free(o_walk);
4642                 o_walk = NULL;
4643                 gtk_text_iter_forward_line(&iter);
4644                 g_free(quote_str);
4645                 quote_str = NULL;
4646                 if (startq_offset != -1) {
4647                         GtkTextIter startquote, endquote;
4648                         gtk_text_buffer_get_iter_at_offset(
4649                                 buffer, &startquote, startq_offset);
4650                         endquote = iter;
4651
4652                         switch (quotelevel) {
4653                         case 0: 
4654                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4655                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4656                                         gtk_text_buffer_apply_tag_by_name(
4657                                                 buffer, "quote0", &startquote, &endquote);
4658                                         gtk_text_buffer_remove_tag_by_name(
4659                                                 buffer, "quote1", &startquote, &endquote);
4660                                         gtk_text_buffer_remove_tag_by_name(
4661                                                 buffer, "quote2", &startquote, &endquote);
4662                                         modified = TRUE;
4663                                 }
4664                                 break;
4665                         case 1: 
4666                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4667                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4668                                         gtk_text_buffer_apply_tag_by_name(
4669                                                 buffer, "quote1", &startquote, &endquote);
4670                                         gtk_text_buffer_remove_tag_by_name(
4671                                                 buffer, "quote0", &startquote, &endquote);
4672                                         gtk_text_buffer_remove_tag_by_name(
4673                                                 buffer, "quote2", &startquote, &endquote);
4674                                         modified = TRUE;
4675                                 }
4676                                 break;
4677                         case 2: 
4678                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4679                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4680                                         gtk_text_buffer_apply_tag_by_name(
4681                                                 buffer, "quote2", &startquote, &endquote);
4682                                         gtk_text_buffer_remove_tag_by_name(
4683                                                 buffer, "quote0", &startquote, &endquote);
4684                                         gtk_text_buffer_remove_tag_by_name(
4685                                                 buffer, "quote1", &startquote, &endquote);
4686                                         modified = TRUE;
4687                                 }
4688                                 break;
4689                         }
4690                         startq_offset = -1;
4691                 } else if (noq_offset != -1) {
4692                         GtkTextIter startnoquote, endnoquote;
4693                         gtk_text_buffer_get_iter_at_offset(
4694                                 buffer, &startnoquote, noq_offset);
4695                         endnoquote = iter;
4696
4697                         if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4698                           && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4699                             (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4700                           && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4701                             (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4702                           && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4703                                 gtk_text_buffer_remove_tag_by_name(
4704                                         buffer, "quote0", &startnoquote, &endnoquote);
4705                                 gtk_text_buffer_remove_tag_by_name(
4706                                         buffer, "quote1", &startnoquote, &endnoquote);
4707                                 gtk_text_buffer_remove_tag_by_name(
4708                                         buffer, "quote2", &startnoquote, &endnoquote);
4709                                 modified = TRUE;
4710                         }
4711                         noq_offset = -1;
4712                 }
4713                 
4714                 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4715                         GtkTextIter nouri_start_iter, nouri_end_iter;
4716                         gtk_text_buffer_get_iter_at_offset(
4717                                 buffer, &nouri_start_iter, nouri_start);
4718                         gtk_text_buffer_get_iter_at_offset(
4719                                 buffer, &nouri_end_iter, nouri_stop);
4720                         if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4721                             gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4722                                 gtk_text_buffer_remove_tag_by_name(
4723                                         buffer, "link", &nouri_start_iter, &nouri_end_iter);
4724                                 modified_before_remove = modified;
4725                                 modified = TRUE;
4726                                 removed = TRUE;
4727                         }
4728                 }
4729                 if (uri_start >= 0 && uri_stop > 0) {
4730                         GtkTextIter uri_start_iter, uri_end_iter, back;
4731                         gtk_text_buffer_get_iter_at_offset(
4732                                 buffer, &uri_start_iter, uri_start);
4733                         gtk_text_buffer_get_iter_at_offset(
4734                                 buffer, &uri_end_iter, uri_stop);
4735                         back = uri_end_iter;
4736                         gtk_text_iter_backward_char(&back);
4737                         if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4738                             !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4739                                 gtk_text_buffer_apply_tag_by_name(
4740                                         buffer, "link", &uri_start_iter, &uri_end_iter);
4741                                 modified = TRUE;
4742                                 if (removed && !modified_before_remove) {
4743                                         modified = FALSE;
4744                                 } 
4745                         }
4746                 }
4747                 if (!modified) {
4748 /*                      debug_print("not modified, out after %d lines\n", lines); */
4749                         goto end;
4750                 }
4751         }
4752 /*      debug_print("modified, out after %d lines\n", lines); */
4753 end:
4754         g_free(itemized_chars);
4755         if (par_iter)
4756                 *par_iter = iter;
4757         undo_wrapping(compose->undostruct, FALSE);
4758         compose->autowrap = prev_autowrap;
4759
4760         return modified;
4761 }
4762
4763 void compose_action_cb(void *data)
4764 {
4765         Compose *compose = (Compose *)data;
4766         compose_wrap_all(compose);
4767 }
4768
4769 static void compose_wrap_all(Compose *compose)
4770 {
4771         compose_wrap_all_full(compose, FALSE);
4772 }
4773
4774 static void compose_wrap_all_full(Compose *compose, gboolean force)
4775 {
4776         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4777         GtkTextBuffer *buffer;
4778         GtkTextIter iter;
4779         gboolean modified = TRUE;
4780
4781         buffer = gtk_text_view_get_buffer(text);
4782
4783         gtk_text_buffer_get_start_iter(buffer, &iter);
4784
4785         undo_wrapping(compose->undostruct, TRUE);
4786
4787         while (!gtk_text_iter_is_end(&iter) && modified)
4788                 modified = compose_beautify_paragraph(compose, &iter, force);
4789
4790         undo_wrapping(compose->undostruct, FALSE);
4791
4792 }
4793
4794 static void compose_set_title(Compose *compose)
4795 {
4796         gchar *str;
4797         gchar *edited;
4798         gchar *subject;
4799         
4800         edited = compose->modified ? _(" [Edited]") : "";
4801         
4802         subject = gtk_editable_get_chars(
4803                         GTK_EDITABLE(compose->subject_entry), 0, -1);
4804
4805 #ifndef GENERIC_UMPC
4806         if (subject && strlen(subject))
4807                 str = g_strdup_printf(_("%s - Compose message%s"),
4808                                       subject, edited); 
4809         else
4810                 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4811 #else
4812         str = g_strdup(_("Compose message"));
4813 #endif
4814
4815         gtk_window_set_title(GTK_WINDOW(compose->window), str);
4816         g_free(str);
4817         g_free(subject);
4818 }
4819
4820 /**
4821  * compose_current_mail_account:
4822  * 
4823  * Find a current mail account (the currently selected account, or the
4824  * default account, if a news account is currently selected).  If a
4825  * mail account cannot be found, display an error message.
4826  * 
4827  * Return value: Mail account, or NULL if not found.
4828  **/
4829 static PrefsAccount *
4830 compose_current_mail_account(void)
4831 {
4832         PrefsAccount *ac;
4833
4834         if (cur_account && cur_account->protocol != A_NNTP)
4835                 ac = cur_account;
4836         else {
4837                 ac = account_get_default();
4838                 if (!ac || ac->protocol == A_NNTP) {
4839                         alertpanel_error(_("Account for sending mail is not specified.\n"
4840                                            "Please select a mail account before sending."));
4841                         return NULL;
4842                 }
4843         }
4844         return ac;
4845 }
4846
4847 #define QUOTE_IF_REQUIRED(out, str)                                     \
4848 {                                                                       \
4849         if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) {           \
4850                 gchar *__tmp;                                           \
4851                 gint len;                                               \
4852                                                                         \
4853                 len = strlen(str) + 3;                                  \
4854                 if ((__tmp = alloca(len)) == NULL) {                    \
4855                         g_warning("can't allocate memory");             \
4856                         g_string_free(header, TRUE);                    \
4857                         return NULL;                                    \
4858                 }                                                       \
4859                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4860                 out = __tmp;                                            \
4861         } else {                                                        \
4862                 gchar *__tmp;                                           \
4863                                                                         \
4864                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4865                         g_warning("can't allocate memory");             \
4866                         g_string_free(header, TRUE);                    \
4867                         return NULL;                                    \
4868                 } else                                                  \
4869                         strcpy(__tmp, str);                             \
4870                                                                         \
4871                 out = __tmp;                                            \
4872         }                                                               \
4873 }
4874
4875 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret)                      \
4876 {                                                                       \
4877         if (*str != '"' && strpbrk(str, ",.:;[]<>()@\\\"")) {           \
4878                 gchar *__tmp;                                           \
4879                 gint len;                                               \
4880                                                                         \
4881                 len = strlen(str) + 3;                                  \
4882                 if ((__tmp = alloca(len)) == NULL) {                    \
4883                         g_warning("can't allocate memory");             \
4884                         errret;                                         \
4885                 }                                                       \
4886                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4887                 out = __tmp;                                            \
4888         } else {                                                        \
4889                 gchar *__tmp;                                           \
4890                                                                         \
4891                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4892                         g_warning("can't allocate memory");             \
4893                         errret;                                         \
4894                 } else                                                  \
4895                         strcpy(__tmp, str);                             \
4896                                                                         \
4897                 out = __tmp;                                            \
4898         }                                                               \
4899 }
4900
4901 static void compose_select_account(Compose *compose, PrefsAccount *account,
4902                                    gboolean init)
4903 {
4904         gchar *from = NULL, *header = NULL;
4905         ComposeHeaderEntry *header_entry;
4906 #if GTK_CHECK_VERSION(2, 24, 0)
4907         GtkTreeIter iter;
4908 #endif
4909
4910         cm_return_if_fail(account != NULL);
4911
4912         compose->account = account;
4913         if (account->name && *account->name) {
4914                 gchar *buf, *qbuf;
4915                 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4916                 qbuf = escape_internal_quotes(buf, '"');
4917                 from = g_strdup_printf("%s <%s>",
4918                                        qbuf, account->address);
4919                 if (qbuf != buf)
4920                         g_free(qbuf);
4921                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4922         } else {
4923                 from = g_strdup_printf("<%s>",
4924                                        account->address);
4925                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4926         }
4927
4928         g_free(from);
4929
4930         compose_set_title(compose);
4931
4932         if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4933                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4934         else
4935                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4936         if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4937                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4938         else
4939                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4940                                        
4941         activate_privacy_system(compose, account, FALSE);
4942
4943         if (!init && compose->mode != COMPOSE_REDIRECT) {
4944                 undo_block(compose->undostruct);
4945                 compose_insert_sig(compose, TRUE);
4946                 undo_unblock(compose->undostruct);
4947         }
4948         
4949         header_entry = (ComposeHeaderEntry *) compose->header_list->data;
4950 #if !GTK_CHECK_VERSION(2, 24, 0)
4951         header = gtk_combo_box_get_active_text(GTK_COMBO_BOX(header_entry->combo));
4952 #else
4953         if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(header_entry->combo), &iter))
4954                 gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(
4955                         header_entry->combo)), &iter, COMBOBOX_TEXT, &header, -1);
4956 #endif
4957         
4958         if (header && !strlen(gtk_entry_get_text(GTK_ENTRY(header_entry->entry)))) {
4959                 if (account->protocol == A_NNTP) {
4960                         if (!strcmp(header, _("To:")))
4961                                 combobox_select_by_text(
4962                                         GTK_COMBO_BOX(header_entry->combo),
4963                                         _("Newsgroups:"));
4964                 } else {
4965                         if (!strcmp(header, _("Newsgroups:")))
4966                                 combobox_select_by_text(
4967                                         GTK_COMBO_BOX(header_entry->combo),
4968                                         _("To:"));
4969                 }
4970                 
4971         }
4972         g_free(header);
4973         
4974 #ifdef USE_ENCHANT
4975         /* use account's dict info if set */
4976         if (compose->gtkaspell) {
4977                 if (account->enable_default_dictionary)
4978                         gtkaspell_change_dict(compose->gtkaspell,
4979                                         account->default_dictionary, FALSE);
4980                 if (account->enable_default_alt_dictionary)
4981                         gtkaspell_change_alt_dict(compose->gtkaspell,
4982                                         account->default_alt_dictionary);
4983                 if (account->enable_default_dictionary
4984                         || account->enable_default_alt_dictionary)
4985                         compose_spell_menu_changed(compose);
4986         }
4987 #endif
4988 }
4989
4990 gboolean compose_check_for_valid_recipient(Compose *compose) {
4991         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4992         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4993         gboolean recipient_found = FALSE;
4994         GSList *list;
4995         gchar **strptr;
4996
4997         /* free to and newsgroup list */
4998         slist_free_strings_full(compose->to_list);
4999         compose->to_list = NULL;
5000                         
5001         slist_free_strings_full(compose->newsgroup_list);
5002         compose->newsgroup_list = NULL;
5003
5004         /* search header entries for to and newsgroup entries */
5005         for (list = compose->header_list; list; list = list->next) {
5006                 gchar *header;
5007                 gchar *entry;
5008                 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
5009                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
5010                 g_strstrip(entry);
5011                 g_strstrip(header);
5012                 if (entry[0] != '\0') {
5013                         for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
5014                                 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
5015                                         compose->to_list = address_list_append(compose->to_list, entry);
5016                                         recipient_found = TRUE;
5017                                 }
5018                         }
5019                         for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
5020                                 if (!g_ascii_strcasecmp(header, prefs_common_translated_header_name(*strptr))) {
5021                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
5022                                         recipient_found = TRUE;
5023                                 }
5024                         }
5025                 }
5026                 g_free(header);
5027                 g_free(entry);
5028         }
5029         return recipient_found;
5030 }
5031
5032 static gboolean compose_check_for_set_recipients(Compose *compose)
5033 {
5034         if (compose->account->set_autocc && compose->account->auto_cc) {
5035                 gboolean found_other = FALSE;
5036                 GSList *list;
5037                 /* search header entries for to and newsgroup entries */
5038                 for (list = compose->header_list; list; list = list->next) {
5039                         gchar *entry;
5040                         gchar *header;
5041                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
5042                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
5043                         g_strstrip(entry);
5044                         g_strstrip(header);
5045                         if (strcmp(entry, compose->account->auto_cc)
5046                         ||  strcmp(header, prefs_common_translated_header_name("Cc:"))) {
5047                                 found_other = TRUE;
5048                                 g_free(entry);
5049                                 break;
5050                         }
5051                         g_free(entry);
5052                         g_free(header);
5053                 }
5054                 if (!found_other) {
5055                         AlertValue aval;
5056                         gchar *text;
5057                         if (compose->batch) {
5058                                 gtk_widget_show_all(compose->window);
5059                         }
5060                         text = g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5061                                            prefs_common_translated_header_name("Cc"));
5062                         aval = alertpanel(_("Send"),
5063                                           text,
5064                                           GTK_STOCK_CANCEL, g_strconcat("+", _("_Send"), NULL), NULL);
5065                         g_free(text);
5066                         if (aval != G_ALERTALTERNATE)
5067                                 return FALSE;
5068                 }
5069         }
5070         if (compose->account->set_autobcc && compose->account->auto_bcc) {
5071                 gboolean found_other = FALSE;
5072                 GSList *list;
5073                 /* search header entries for to and newsgroup entries */
5074                 for (list = compose->header_list; list; list = list->next) {
5075                         gchar *entry;
5076                         gchar *header;
5077                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
5078                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
5079                         g_strstrip(entry);
5080                         g_strstrip(header);
5081                         if (strcmp(entry, compose->account->auto_bcc)
5082                         ||  strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
5083                                 found_other = TRUE;
5084                                 g_free(entry);
5085                                 g_free(header);
5086                                 break;
5087                         }
5088                         g_free(entry);
5089                         g_free(header);
5090                 }
5091                 if (!found_other) {
5092                         AlertValue aval;
5093                         gchar *text;
5094                         if (compose->batch) {
5095                                 gtk_widget_show_all(compose->window);
5096                         }
5097                         text = g_strdup_printf(_("The only recipient is the default '%s' address. Send anyway?"),
5098                                            prefs_common_translated_header_name("Bcc"));
5099                         aval = alertpanel(_("Send"),
5100                                           text,
5101                                           GTK_STOCK_CANCEL, g_strconcat("+", _("_Send"), NULL), NULL);
5102                         g_free(text);
5103                         if (aval != G_ALERTALTERNATE)
5104                                 return FALSE;
5105                 }
5106         }
5107         return TRUE;
5108 }
5109
5110 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
5111 {
5112         const gchar *str;
5113
5114         if (compose_check_for_valid_recipient(compose) == FALSE) {
5115                 if (compose->batch) {
5116                         gtk_widget_show_all(compose->window);
5117                 }
5118                 alertpanel_error(_("Recipient is not specified."));
5119                 return FALSE;
5120         }
5121
5122         if (compose_check_for_set_recipients(compose) == FALSE) {
5123                 return FALSE;
5124         }
5125
5126         if (!compose->batch && prefs_common.warn_empty_subj == TRUE) {
5127                 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5128                 if (*str == '\0' && check_everything == TRUE &&
5129                     compose->mode != COMPOSE_REDIRECT) {
5130                         AlertValue aval;
5131                         gchar *button_label;
5132                         gchar *message;
5133
5134                         if (compose->sending)
5135                                 button_label = g_strconcat("+", _("_Send"), NULL);
5136                         else
5137                                 button_label = g_strconcat("+", _("_Queue"), NULL);
5138                         message = g_strdup_printf(_("Subject is empty. %s"),
5139                                         compose->sending?_("Send it anyway?"):
5140                                         _("Queue it anyway?"));
5141
5142                         aval = alertpanel_full(compose->sending?_("Send"):_("Send later"), message,
5143                                                GTK_STOCK_CANCEL, button_label, NULL, TRUE, NULL,
5144                                                ALERT_QUESTION, G_ALERTDEFAULT);
5145                         g_free(message);
5146                         if (aval & G_ALERTDISABLE) {
5147                                 aval &= ~G_ALERTDISABLE;
5148                                 prefs_common.warn_empty_subj = FALSE;
5149                         }
5150                         if (aval != G_ALERTALTERNATE)
5151                                 return FALSE;
5152                 }
5153         }
5154
5155         if (!compose->batch && prefs_common.warn_sending_many_recipients_num > 0
5156                         && check_everything == TRUE) {
5157                 GSList *list;
5158                 gint cnt = 0;
5159
5160                 /* count To and Cc recipients */
5161                 for (list = compose->header_list; list; list = list->next) {
5162                         gchar *header;
5163                         gchar *entry;
5164
5165                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
5166                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
5167                         g_strstrip(header);
5168                         g_strstrip(entry);
5169                         if ((entry[0] != '\0')
5170                         &&      (strcmp(header, prefs_common_translated_header_name("To:"))
5171                         ||  strcmp(header, prefs_common_translated_header_name("Cc:")))) {
5172                                 cnt++;
5173                         }
5174                         g_free(header);
5175                         g_free(entry);
5176                 }
5177                 if (cnt > prefs_common.warn_sending_many_recipients_num) {
5178                         AlertValue aval;
5179                         gchar *button_label;
5180                         gchar *message;
5181
5182                         if (compose->sending)
5183                                 button_label = g_strconcat("+", _("_Send"), NULL);
5184                         else
5185                                 button_label = g_strconcat("+", _("_Queue"), NULL);
5186                         message = g_strdup_printf(_("Sending to %d recipients. %s"), cnt,
5187                                         compose->sending?_("Send it anyway?"):
5188                                         _("Queue it anyway?"));
5189
5190                         aval = alertpanel_full(compose->sending?_("Send"):_("Send later"), message,
5191                                                GTK_STOCK_CANCEL, button_label, NULL, TRUE, NULL,
5192                                                ALERT_QUESTION, G_ALERTDEFAULT);
5193                         g_free(message);
5194                         if (aval & G_ALERTDISABLE) {
5195                                 aval &= ~G_ALERTDISABLE;
5196                                 prefs_common.warn_sending_many_recipients_num = 0;
5197                         }
5198                         if (aval != G_ALERTALTERNATE)
5199                                 return FALSE;
5200                 }
5201         }
5202
5203         if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
5204                 return FALSE;
5205
5206         return TRUE;
5207 }
5208
5209 gint compose_send(Compose *compose)
5210 {
5211         gint msgnum;
5212         FolderItem *folder = NULL;
5213         gint val = -1;
5214         gchar *msgpath = NULL;
5215         gboolean discard_window = FALSE;
5216         gchar *errstr = NULL;
5217         gchar *tmsgid = NULL;
5218         MainWindow *mainwin = mainwindow_get_mainwindow();
5219         gboolean queued_removed = FALSE;
5220
5221         if (prefs_common.send_dialog_invisible
5222                         || compose->batch == TRUE)
5223                 discard_window = TRUE;
5224
5225         compose_allow_user_actions (compose, FALSE);
5226         compose->sending = TRUE;
5227
5228         if (compose_check_entries(compose, TRUE) == FALSE) {
5229                 if (compose->batch) {
5230                         gtk_widget_show_all(compose->window);
5231                 }
5232                 goto bail;
5233         }
5234
5235         inc_lock();
5236         val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5237
5238         if (val) {
5239                 if (compose->batch) {
5240                         gtk_widget_show_all(compose->window);
5241                 }
5242                 if (val == -4) {
5243                         alertpanel_error(_("Could not queue message for sending:\n\n"
5244                                            "Charset conversion failed."));
5245                 } else if (val == -5) {
5246                         alertpanel_error(_("Could not queue message for sending:\n\n"
5247                                            "Couldn't get recipient encryption key."));
5248                 } else if (val == -6) {
5249                         /* silent error */
5250                 } else if (val == -3) {
5251                         if (privacy_peek_error())
5252                         alertpanel_error(_("Could not queue message for sending:\n\n"
5253                                            "Signature failed: %s"), privacy_get_error());
5254                 } else if (val == -2 && errno != 0) {
5255                         alertpanel_error(_("Could not queue message for sending:\n\n%s."), g_strerror(errno));
5256                 } else {
5257                         alertpanel_error(_("Could not queue message for sending."));
5258                 }
5259                 goto bail;
5260         }
5261
5262         tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5263         if (discard_window) {
5264                 compose->sending = FALSE;
5265                 compose_close(compose);
5266                 /* No more compose access in the normal codepath 
5267                  * after this point! */
5268                 compose = NULL;
5269         }
5270
5271         if (msgnum == 0) {
5272                 alertpanel_error(_("The message was queued but could not be "
5273                                    "sent.\nUse \"Send queued messages\" from "
5274                                    "the main window to retry."));
5275                 if (!discard_window) {
5276                         goto bail;
5277                 }
5278                 inc_unlock();
5279                 g_free(tmsgid);
5280                 return -1;
5281         }
5282         if (msgpath == NULL) {
5283                 msgpath = folder_item_fetch_msg(folder, msgnum);
5284                 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5285                 g_free(msgpath);
5286         } else {
5287                 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5288                 claws_unlink(msgpath);
5289                 g_free(msgpath);
5290         }
5291         if (!discard_window) {
5292                 if (val != 0) {
5293                         if (!queued_removed)
5294                                 folder_item_remove_msg(folder, msgnum);
5295                         folder_item_scan(folder);
5296                         if (tmsgid) {
5297                                 /* make sure we delete that */
5298                                 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5299                                 if (tmp) {
5300                                         debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5301                                         folder_item_remove_msg(folder, tmp->msgnum);
5302                                         procmsg_msginfo_free(&tmp);
5303                                 } 
5304                         }
5305                 }
5306         }
5307
5308         if (val == 0) {
5309                 if (!queued_removed)
5310                         folder_item_remove_msg(folder, msgnum);
5311                 folder_item_scan(folder);
5312                 if (tmsgid) {
5313                         /* make sure we delete that */
5314                         MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5315                         if (tmp) {
5316                                 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5317                                 folder_item_remove_msg(folder, tmp->msgnum);
5318                                 procmsg_msginfo_free(&tmp);
5319                         }
5320                 }
5321                 if (!discard_window) {
5322                         compose->sending = FALSE;
5323                         compose_allow_user_actions (compose, TRUE);
5324                         compose_close(compose);
5325                 }
5326         } else {
5327                 if (errstr) {
5328                         alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5329                                    "the main window to retry."), errstr);
5330                         g_free(errstr);
5331                 } else {
5332                         alertpanel_error_log(_("The message was queued but could not be "
5333                                    "sent.\nUse \"Send queued messages\" from "
5334                                    "the main window to retry."));
5335                 }
5336                 if (!discard_window) {
5337                         goto bail;              
5338                 }
5339                 inc_unlock();
5340                 g_free(tmsgid);
5341                 return -1;
5342         }
5343         g_free(tmsgid);
5344         inc_unlock();
5345         toolbar_main_set_sensitive(mainwin);
5346         main_window_set_menu_sensitive(mainwin);
5347         return 0;
5348
5349 bail:
5350         inc_unlock();
5351         g_free(tmsgid);
5352         compose_allow_user_actions (compose, TRUE);
5353         compose->sending = FALSE;
5354         compose->modified = TRUE; 
5355         toolbar_main_set_sensitive(mainwin);
5356         main_window_set_menu_sensitive(mainwin);
5357
5358         return -1;
5359 }
5360
5361 static gboolean compose_use_attach(Compose *compose) 
5362 {
5363         GtkTreeModel *model = gtk_tree_view_get_model
5364                                 (GTK_TREE_VIEW(compose->attach_clist));
5365         return gtk_tree_model_iter_n_children(model, NULL) > 0;
5366 }
5367
5368 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
5369                                                            FILE *fp)
5370 {
5371         gchar buf[BUFFSIZE];
5372         gchar *str;
5373         gboolean first_to_address;
5374         gboolean first_cc_address;
5375         GSList *list;
5376         ComposeHeaderEntry *headerentry;
5377         const gchar *headerentryname;
5378         const gchar *cc_hdr;
5379         const gchar *to_hdr;
5380         gboolean err = FALSE;
5381
5382         debug_print("Writing redirect header\n");
5383
5384         cc_hdr = prefs_common_translated_header_name("Cc:");
5385         to_hdr = prefs_common_translated_header_name("To:");
5386
5387         first_to_address = TRUE;
5388         for (list = compose->header_list; list; list = list->next) {
5389                 headerentry = ((ComposeHeaderEntry *)list->data);
5390                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5391
5392                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5393                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5394                         Xstrdup_a(str, entstr, return -1);
5395                         g_strstrip(str);
5396                         if (str[0] != '\0') {
5397                                 compose_convert_header
5398                                         (compose, buf, sizeof(buf), str,
5399                                         strlen("Resent-To") + 2, TRUE);
5400
5401                                 if (first_to_address) {
5402                                         err |= (fprintf(fp, "Resent-To: ") < 0);
5403                                         first_to_address = FALSE;
5404                                 } else {
5405                                         err |= (fprintf(fp, ",") < 0);
5406                                 }
5407                                 err |= (fprintf(fp, "%s", buf) < 0);
5408                         }
5409                 }
5410         }
5411         if (!first_to_address) {
5412                 err |= (fprintf(fp, "\n") < 0);
5413         }
5414
5415         first_cc_address = TRUE;
5416         for (list = compose->header_list; list; list = list->next) {
5417                 headerentry = ((ComposeHeaderEntry *)list->data);
5418                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5419
5420                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5421                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5422                         Xstrdup_a(str, strg, return -1);
5423                         g_strstrip(str);
5424                         if (str[0] != '\0') {
5425                                 compose_convert_header
5426                                         (compose, buf, sizeof(buf), str,
5427                                         strlen("Resent-Cc") + 2, TRUE);
5428
5429                                 if (first_cc_address) {
5430                                         err |= (fprintf(fp, "Resent-Cc: ") < 0);
5431                                         first_cc_address = FALSE;
5432                                 } else {
5433                                         err |= (fprintf(fp, ",") < 0);
5434                                 }
5435                                 err |= (fprintf(fp, "%s", buf) < 0);
5436                         }
5437                 }
5438         }
5439         if (!first_cc_address) {
5440                 err |= (fprintf(fp, "\n") < 0);
5441         }
5442         
5443         return (err ? -1:0);
5444 }
5445
5446 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5447 {
5448         gchar date[RFC822_DATE_BUFFSIZE];
5449         gchar buf[BUFFSIZE];
5450         gchar *str;
5451         const gchar *entstr;
5452         /* struct utsname utsbuf; */
5453         gboolean err = FALSE;
5454
5455         cm_return_val_if_fail(fp != NULL, -1);
5456         cm_return_val_if_fail(compose->account != NULL, -1);
5457         cm_return_val_if_fail(compose->account->address != NULL, -1);
5458
5459         /* Resent-Date */
5460         if (prefs_common.hide_timezone)
5461                 get_rfc822_date_hide_tz(date, sizeof(date));
5462         else
5463                 get_rfc822_date(date, sizeof(date));
5464         err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5465
5466         /* Resent-From */
5467         if (compose->account->name && *compose->account->name) {
5468                 compose_convert_header
5469                         (compose, buf, sizeof(buf), compose->account->name,
5470                          strlen("From: "), TRUE);
5471                 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5472                         buf, compose->account->address) < 0);
5473         } else
5474                 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5475
5476         /* Subject */
5477         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5478         if (*entstr != '\0') {
5479                 Xstrdup_a(str, entstr, return -1);
5480                 g_strstrip(str);
5481                 if (*str != '\0') {
5482                         compose_convert_header(compose, buf, sizeof(buf), str,
5483                                                strlen("Subject: "), FALSE);
5484                         err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5485                 }
5486         }
5487
5488         /* Resent-Message-ID */
5489         if (compose->account->gen_msgid) {
5490                 gchar *addr = prefs_account_generate_msgid(compose->account);
5491                 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", addr) < 0);
5492                 if (compose->msgid)
5493                         g_free(compose->msgid);
5494                 compose->msgid = addr;
5495         } else {
5496                 compose->msgid = NULL;
5497         }
5498
5499         if (compose_redirect_write_headers_from_headerlist(compose, fp))
5500                 return -1;
5501
5502         /* separator between header and body */
5503         err |= (fputs("\n", fp) == EOF);
5504
5505         return (err ? -1:0);
5506 }
5507
5508 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5509 {
5510         FILE *fp;
5511         size_t len;
5512         gchar *buf = NULL;
5513         gchar rewrite_buf[BUFFSIZE];
5514         int i = 0;
5515         gboolean skip = FALSE;
5516         gboolean err = FALSE;
5517         gchar *not_included[]={
5518                 "Return-Path:",         "Delivered-To:",        "Received:",
5519                 "Subject:",             "X-UIDL:",              "AF:",
5520                 "NF:",                  "PS:",                  "SRH:",
5521                 "SFN:",                 "DSR:",                 "MID:",
5522                 "CFG:",                 "PT:",                  "S:",
5523                 "RQ:",                  "SSV:",                 "NSV:",
5524                 "SSH:",                 "R:",                   "MAID:",
5525                 "NAID:",                "RMID:",                "FMID:",
5526                 "SCF:",                 "RRCPT:",               "NG:",
5527                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
5528                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
5529                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
5530                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
5531                 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5532                 NULL
5533                 };
5534         gint ret = 0;
5535
5536         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5537                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5538                 return -1;
5539         }
5540
5541         while ((ret = procheader_get_one_field_asis(&buf, fp)) != -1) {
5542                 skip = FALSE;
5543                 for (i = 0; not_included[i] != NULL; i++) {
5544                         if (g_ascii_strncasecmp(buf, not_included[i],
5545                                                 strlen(not_included[i])) == 0) {
5546                                 skip = TRUE;
5547                                 break;
5548                         }
5549                 }
5550                 if (skip) {
5551                         g_free(buf);
5552                         buf = NULL;
5553                         continue;
5554                 }
5555                 if (fputs(buf, fdest) == -1) {
5556                         g_free(buf);
5557                         buf = NULL;
5558                         goto error;
5559                 }
5560
5561                 if (!prefs_common.redirect_keep_from) {
5562                         if (g_ascii_strncasecmp(buf, "From:",
5563                                           strlen("From:")) == 0) {
5564                                 err |= (fputs(" (by way of ", fdest) == EOF);
5565                                 if (compose->account->name
5566                                     && *compose->account->name) {
5567                                         gchar buffer[BUFFSIZE];
5568
5569                                         compose_convert_header
5570                                                 (compose, buffer, sizeof(buffer),
5571                                                  compose->account->name,
5572                                                  strlen("From: "),
5573                                                  FALSE);
5574                                         err |= (fprintf(fdest, "%s <%s>",
5575                                                 buffer,
5576                                                 compose->account->address) < 0);
5577                                 } else
5578                                         err |= (fprintf(fdest, "%s",
5579                                                 compose->account->address) < 0);
5580                                 err |= (fputs(")", fdest) == EOF);
5581                         }
5582                 }
5583
5584                 g_free(buf);
5585                 buf = NULL;
5586                 if (fputs("\n", fdest) == -1)
5587                         goto error;
5588         }
5589
5590         if (err)
5591                 goto error;
5592
5593         if (compose_redirect_write_headers(compose, fdest))
5594                 goto error;
5595
5596         while ((len = fread(rewrite_buf, sizeof(gchar), sizeof(rewrite_buf), fp)) > 0) {
5597                 if (fwrite(rewrite_buf, sizeof(gchar), len, fdest) != len)
5598                         goto error;
5599         }
5600
5601         fclose(fp);
5602
5603         return 0;
5604
5605 error:
5606         fclose(fp);
5607
5608         return -1;
5609 }
5610
5611 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5612 {
5613         GtkTextBuffer *buffer;
5614         GtkTextIter start, end, tmp;
5615         gchar *chars, *tmp_enc_file, *content;
5616         gchar *buf, *msg;
5617         const gchar *out_codeset;
5618         EncodingType encoding = ENC_UNKNOWN;
5619         MimeInfo *mimemsg, *mimetext;
5620         gint line;
5621         const gchar *src_codeset = CS_INTERNAL;
5622         gchar *from_addr = NULL;
5623         gchar *from_name = NULL;
5624         FolderItem *outbox;
5625
5626         if (action == COMPOSE_WRITE_FOR_SEND) {
5627                 attach_parts = TRUE;
5628
5629                 /* We're sending the message, generate a Message-ID
5630                  * if necessary. */
5631                 if (compose->msgid == NULL &&
5632                                 compose->account->gen_msgid) {
5633                         compose->msgid = prefs_account_generate_msgid(compose->account);
5634                 }
5635         }
5636
5637         /* create message MimeInfo */
5638         mimemsg = procmime_mimeinfo_new();
5639         mimemsg->type = MIMETYPE_MESSAGE;
5640         mimemsg->subtype = g_strdup("rfc822");
5641         mimemsg->content = MIMECONTENT_MEM;
5642         mimemsg->tmp = TRUE; /* must free content later */
5643         mimemsg->data.mem = compose_get_header(compose);
5644
5645         /* Create text part MimeInfo */
5646         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5647         gtk_text_buffer_get_end_iter(buffer, &end);
5648         tmp = end;
5649
5650         /* We make sure that there is a newline at the end. */
5651         if (action == COMPOSE_WRITE_FOR_SEND && gtk_text_iter_backward_char(&tmp)) {
5652                 chars = gtk_text_buffer_get_text(buffer, &tmp, &end, FALSE);
5653                 if (*chars != '\n') {
5654                         gtk_text_buffer_insert(buffer, &end, "\n", 1);
5655                 }
5656         }
5657
5658         /* get all composed text */
5659         gtk_text_buffer_get_start_iter(buffer, &start);
5660         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5661
5662         out_codeset = conv_get_charset_str(compose->out_encoding);
5663
5664         if (!out_codeset && is_ascii_str(chars)) {
5665                 out_codeset = CS_US_ASCII;
5666         } else if (prefs_common.outgoing_fallback_to_ascii &&
5667                    is_ascii_str(chars)) {
5668                 out_codeset = CS_US_ASCII;
5669                 encoding = ENC_7BIT;
5670         }
5671
5672         if (!out_codeset) {
5673                 gchar *test_conv_global_out = NULL;
5674                 gchar *test_conv_reply = NULL;
5675
5676                 /* automatic mode. be automatic. */
5677                 codeconv_set_strict(TRUE);
5678
5679                 out_codeset = conv_get_outgoing_charset_str();
5680                 if (out_codeset) {
5681                         debug_print("trying to convert to %s\n", out_codeset);
5682                         test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5683                 }
5684
5685                 if (!test_conv_global_out && compose->orig_charset
5686                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5687                         out_codeset = compose->orig_charset;
5688                         debug_print("failure; trying to convert to %s\n", out_codeset);
5689                         test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5690                 }
5691
5692                 if (!test_conv_global_out && !test_conv_reply) {
5693                         /* we're lost */
5694                         out_codeset = CS_INTERNAL;
5695                         debug_print("failure; finally using %s\n", out_codeset);
5696                 }
5697                 g_free(test_conv_global_out);
5698                 g_free(test_conv_reply);
5699                 codeconv_set_strict(FALSE);
5700         }
5701
5702         if (encoding == ENC_UNKNOWN) {
5703                 if (prefs_common.encoding_method == CTE_BASE64)
5704                         encoding = ENC_BASE64;
5705                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5706                         encoding = ENC_QUOTED_PRINTABLE;
5707                 else if (prefs_common.encoding_method == CTE_8BIT)
5708                         encoding = ENC_8BIT;
5709                 else
5710                         encoding = procmime_get_encoding_for_charset(out_codeset);
5711         }
5712
5713         debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5714                     src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5715
5716         if (action == COMPOSE_WRITE_FOR_SEND) {
5717                 codeconv_set_strict(TRUE);
5718                 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5719                 codeconv_set_strict(FALSE);
5720
5721                 if (!buf) {
5722                         AlertValue aval;
5723
5724                         msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5725                                                 "to the specified %s charset.\n"
5726                                                 "Send it as %s?"), out_codeset, src_codeset);
5727                         aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL,
5728                                                g_strconcat("+", _("_Send"), NULL), NULL, FALSE,
5729                                               NULL, ALERT_ERROR, G_ALERTDEFAULT);
5730                         g_free(msg);
5731
5732                         if (aval != G_ALERTALTERNATE) {
5733                                 g_free(chars);
5734                                 return -3;
5735                         } else {
5736                                 buf = chars;
5737                                 out_codeset = src_codeset;
5738                                 chars = NULL;
5739                         }
5740                 }
5741         } else {
5742                 buf = chars;
5743                 out_codeset = src_codeset;
5744                 chars = NULL;
5745         }
5746         g_free(chars);
5747
5748         if (prefs_common.rewrite_first_from && (encoding == ENC_8BIT || encoding == ENC_7BIT)) {
5749                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5750                     strstr(buf, "\nFrom ") != NULL) {
5751                         encoding = ENC_QUOTED_PRINTABLE;
5752                 }
5753         }
5754
5755         mimetext = procmime_mimeinfo_new();
5756         mimetext->content = MIMECONTENT_MEM;
5757         mimetext->tmp = TRUE; /* must free content later */
5758         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5759          * and free the data, which we need later. */
5760         mimetext->data.mem = g_strdup(buf); 
5761         mimetext->type = MIMETYPE_TEXT;
5762         mimetext->subtype = g_strdup("plain");
5763         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5764                             g_strdup(out_codeset));
5765                             
5766         /* protect trailing spaces when signing message */
5767         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5768             privacy_system_can_sign(compose->privacy_system)) {
5769                 encoding = ENC_QUOTED_PRINTABLE;
5770         }
5771
5772 #ifdef G_OS_WIN32
5773         debug_print("main text: %Id bytes encoded as %s in %d\n",
5774 #else
5775         debug_print("main text: %zd bytes encoded as %s in %d\n",
5776 #endif
5777                 strlen(buf), out_codeset, encoding);
5778
5779         /* check for line length limit */
5780         if (action == COMPOSE_WRITE_FOR_SEND &&
5781             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5782             check_line_length(buf, 1000, &line) < 0) {
5783                 AlertValue aval;
5784
5785                 msg = g_strdup_printf
5786                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5787                            "The contents of the message might be broken on the way to the delivery.\n"
5788                            "\n"
5789                            "Send it anyway?"), line + 1);
5790                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5791                 g_free(msg);
5792                 if (aval != G_ALERTALTERNATE) {
5793                         g_free(buf);
5794                         return -1;
5795                 }
5796         }
5797         
5798         if (encoding != ENC_UNKNOWN)
5799                 procmime_encode_content(mimetext, encoding);
5800
5801         /* append attachment parts */
5802         if (compose_use_attach(compose) && attach_parts) {
5803                 MimeInfo *mimempart;
5804                 gchar *boundary = NULL;
5805                 mimempart = procmime_mimeinfo_new();
5806                 mimempart->content = MIMECONTENT_EMPTY;
5807                 mimempart->type = MIMETYPE_MULTIPART;
5808                 mimempart->subtype = g_strdup("mixed");
5809
5810                 do {
5811                         g_free(boundary);
5812                         boundary = generate_mime_boundary(NULL);
5813                 } while (strstr(buf, boundary) != NULL);
5814
5815                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5816                                     boundary);
5817
5818                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5819
5820                 g_node_append(mimempart->node, mimetext->node);
5821                 g_node_append(mimemsg->node, mimempart->node);
5822
5823                 if (compose_add_attachments(compose, mimempart) < 0)
5824                         return -1;
5825         } else
5826                 g_node_append(mimemsg->node, mimetext->node);
5827
5828         g_free(buf);
5829
5830         if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5831                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5832                 /* extract name and address */
5833                 if (strstr(spec, " <") && strstr(spec, ">")) {
5834                         from_addr = g_strdup(strrchr(spec, '<')+1);
5835                         *(strrchr(from_addr, '>')) = '\0';
5836                         from_name = g_strdup(spec);
5837                         *(strrchr(from_name, '<')) = '\0';
5838                 } else {
5839                         from_name = NULL;
5840                         from_addr = NULL;
5841                 }
5842                 g_free(spec);
5843         }
5844         /* sign message if sending */
5845         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5846             privacy_system_can_sign(compose->privacy_system))
5847                 if (!privacy_sign(compose->privacy_system, mimemsg, 
5848                         compose->account, from_addr)) {
5849                         g_free(from_name);
5850                         g_free(from_addr);
5851                         return -2;
5852         }
5853         g_free(from_name);
5854         g_free(from_addr);
5855
5856         if (compose->use_encryption) {
5857                 if (compose->encdata != NULL &&
5858                                 strcmp(compose->encdata, "_DONT_ENCRYPT_")) {
5859
5860                         /* First, write an unencrypted copy and save it to outbox, if
5861                          * user wants that. */
5862                         if (compose->account->save_encrypted_as_clear_text) {
5863                                 debug_print("saving sent message unencrypted...\n");
5864                                 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
5865                                 if (tmpfp) {
5866                                         fclose(tmpfp);
5867
5868                                         /* fp now points to a file with headers written,
5869                                          * let's make a copy. */
5870                                         rewind(fp);
5871                                         content = file_read_stream_to_str(fp);
5872
5873                                         str_write_to_file(content, tmp_enc_file);
5874                                         g_free(content);
5875
5876                                         /* Now write the unencrypted body. */
5877                                         if ((tmpfp = g_fopen(tmp_enc_file, "a")) != NULL) {
5878                                                 procmime_write_mimeinfo(mimemsg, tmpfp);
5879                                                 fclose(tmpfp);
5880
5881                                                 outbox = folder_find_item_from_identifier(compose_get_save_to(compose));
5882                                                 if (!outbox)
5883                                                         outbox = folder_get_default_outbox();
5884
5885                                                 procmsg_save_to_outbox(outbox, tmp_enc_file, TRUE);
5886                                                 claws_unlink(tmp_enc_file);
5887                                         } else {
5888                                                 g_warning("Can't open file '%s'", tmp_enc_file);
5889                                         }
5890                                 } else {
5891                                         g_warning("couldn't get tempfile");
5892                                 }
5893                         }
5894                         if (!privacy_encrypt(compose->privacy_system, mimemsg, compose->encdata)) {
5895                                 debug_print("Couldn't encrypt mime structure: %s.\n",
5896                                                 privacy_get_error());
5897                                 alertpanel_error(_("Couldn't encrypt the email: %s"),
5898                                                 privacy_get_error());
5899                         }
5900                 }
5901         }
5902
5903         procmime_write_mimeinfo(mimemsg, fp);
5904         
5905         procmime_mimeinfo_free_all(&mimemsg);
5906
5907         return 0;
5908 }
5909
5910 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5911 {
5912         GtkTextBuffer *buffer;
5913         GtkTextIter start, end;
5914         FILE *fp;
5915         size_t len;
5916         gchar *chars, *tmp;
5917
5918         if ((fp = g_fopen(file, "wb")) == NULL) {
5919                 FILE_OP_ERROR(file, "fopen");
5920                 return -1;
5921         }
5922
5923         /* chmod for security */
5924         if (change_file_mode_rw(fp, file) < 0) {
5925                 FILE_OP_ERROR(file, "chmod");
5926                 g_warning("can't change file mode");
5927         }
5928
5929         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5930         gtk_text_buffer_get_start_iter(buffer, &start);
5931         gtk_text_buffer_get_end_iter(buffer, &end);
5932         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5933
5934         chars = conv_codeset_strdup
5935                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5936
5937         g_free(tmp);
5938         if (!chars) {
5939                 fclose(fp);
5940                 claws_unlink(file);
5941                 return -1;
5942         }
5943         /* write body */
5944         len = strlen(chars);
5945         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5946                 FILE_OP_ERROR(file, "fwrite");
5947                 g_free(chars);
5948                 fclose(fp);
5949                 claws_unlink(file);
5950                 return -1;
5951         }
5952
5953         g_free(chars);
5954
5955         if (fclose(fp) == EOF) {
5956                 FILE_OP_ERROR(file, "fclose");
5957                 claws_unlink(file);
5958                 return -1;
5959         }
5960         return 0;
5961 }
5962
5963 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5964 {
5965         FolderItem *item;
5966         MsgInfo *msginfo = compose->targetinfo;
5967
5968         cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5969         if (!msginfo) return -1;
5970
5971         if (!force && MSG_IS_LOCKED(msginfo->flags))
5972                 return 0;
5973
5974         item = msginfo->folder;
5975         cm_return_val_if_fail(item != NULL, -1);
5976
5977         if (procmsg_msg_exist(msginfo) &&
5978             (folder_has_parent_of_type(item, F_QUEUE) ||
5979              folder_has_parent_of_type(item, F_DRAFT) 
5980              || msginfo == compose->autosaved_draft)) {
5981                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5982                         g_warning("can't remove the old message");
5983                         return -1;
5984                 } else {
5985                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5986                 }
5987         }
5988
5989         return 0;
5990 }
5991
5992 static void compose_remove_draft(Compose *compose)
5993 {
5994         FolderItem *drafts;
5995         MsgInfo *msginfo = compose->targetinfo;
5996         drafts = account_get_special_folder(compose->account, F_DRAFT);
5997
5998         if (procmsg_msg_exist(msginfo)) {
5999                 folder_item_remove_msg(drafts, msginfo->msgnum);
6000         }
6001
6002 }
6003
6004 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
6005                    gboolean remove_reedit_target)
6006 {
6007         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
6008 }
6009
6010 static gboolean compose_warn_encryption(Compose *compose)
6011 {
6012         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
6013         AlertValue val = G_ALERTALTERNATE;
6014         
6015         if (warning == NULL)
6016                 return TRUE;
6017
6018         val = alertpanel_full(_("Encryption warning"), warning,
6019                   GTK_STOCK_CANCEL, g_strconcat("+", _("C_ontinue"), NULL), NULL,
6020                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
6021         if (val & G_ALERTDISABLE) {
6022                 val &= ~G_ALERTDISABLE;
6023                 if (val == G_ALERTALTERNATE)
6024                         privacy_inhibit_encrypt_warning(compose->privacy_system,
6025                                 TRUE);
6026         }
6027
6028         if (val == G_ALERTALTERNATE) {
6029                 return TRUE;
6030         } else {
6031                 return FALSE;
6032         } 
6033 }
6034
6035 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
6036                               gchar **msgpath, gboolean perform_checks,
6037                               gboolean remove_reedit_target)
6038 {
6039         FolderItem *queue;
6040         gchar *tmp;
6041         FILE *fp;
6042         GSList *cur;
6043         gint num;
6044         PrefsAccount *mailac = NULL, *newsac = NULL;
6045         gboolean err = FALSE;
6046
6047         debug_print("queueing message...\n");
6048         cm_return_val_if_fail(compose->account != NULL, -1);
6049
6050         if (compose_check_entries(compose, perform_checks) == FALSE) {
6051                 if (compose->batch) {
6052                         gtk_widget_show_all(compose->window);
6053                 }
6054                 return -1;
6055         }
6056
6057         if (!compose->to_list && !compose->newsgroup_list) {
6058                 g_warning("can't get recipient list.");
6059                 return -1;
6060         }
6061
6062         if (compose->to_list) {
6063                 if (compose->account->protocol != A_NNTP)
6064                         mailac = compose->account;
6065                 else if (cur_account && cur_account->protocol != A_NNTP)
6066                         mailac = cur_account;
6067                 else if (!(mailac = compose_current_mail_account())) {
6068                         alertpanel_error(_("No account for sending mails available!"));
6069                         return -1;
6070                 }
6071         }
6072
6073         if (compose->newsgroup_list) {
6074                 if (compose->account->protocol == A_NNTP)
6075                         newsac = compose->account;
6076                 else {
6077                         alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
6078                         return -1;
6079                 }                       
6080         }
6081
6082         /* write queue header */
6083         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
6084                               G_DIR_SEPARATOR, compose, (guint) rand());
6085         debug_print("queuing to %s\n", tmp);
6086         if ((fp = g_fopen(tmp, "w+b")) == NULL) {
6087                 FILE_OP_ERROR(tmp, "fopen");
6088                 g_free(tmp);
6089                 return -2;
6090         }
6091
6092         if (change_file_mode_rw(fp, tmp) < 0) {
6093                 FILE_OP_ERROR(tmp, "chmod");
6094                 g_warning("can't change file mode");
6095         }
6096
6097         /* queueing variables */
6098         err |= (fprintf(fp, "AF:\n") < 0);
6099         err |= (fprintf(fp, "NF:0\n") < 0);
6100         err |= (fprintf(fp, "PS:10\n") < 0);
6101         err |= (fprintf(fp, "SRH:1\n") < 0);
6102         err |= (fprintf(fp, "SFN:\n") < 0);
6103         err |= (fprintf(fp, "DSR:\n") < 0);
6104         if (compose->msgid)
6105                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
6106         else
6107                 err |= (fprintf(fp, "MID:\n") < 0);
6108         err |= (fprintf(fp, "CFG:\n") < 0);
6109         err |= (fprintf(fp, "PT:0\n") < 0);
6110         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
6111         err |= (fprintf(fp, "RQ:\n") < 0);
6112         if (mailac)
6113                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
6114         else
6115                 err |= (fprintf(fp, "SSV:\n") < 0);
6116         if (newsac)
6117                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
6118         else
6119                 err |= (fprintf(fp, "NSV:\n") < 0);
6120         err |= (fprintf(fp, "SSH:\n") < 0);
6121         /* write recepient list */
6122         if (compose->to_list) {
6123                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
6124                 for (cur = compose->to_list->next; cur != NULL;
6125                      cur = cur->next)
6126                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
6127                 err |= (fprintf(fp, "\n") < 0);
6128         }
6129         /* write newsgroup list */
6130         if (compose->newsgroup_list) {
6131                 err |= (fprintf(fp, "NG:") < 0);
6132                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
6133                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
6134                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
6135                 err |= (fprintf(fp, "\n") < 0);
6136         }
6137         /* Sylpheed account IDs */
6138         if (mailac)
6139                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
6140         if (newsac)
6141                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
6142
6143         
6144         if (compose->privacy_system != NULL) {
6145                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
6146                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
6147                 if (compose->use_encryption) {
6148                         if (!compose_warn_encryption(compose)) {
6149                                 fclose(fp);
6150                                 claws_unlink(tmp);
6151                                 g_free(tmp);
6152                                 return -6;
6153                         }
6154                         if (mailac && mailac->encrypt_to_self) {
6155                                 GSList *tmp_list = g_slist_copy(compose->to_list);
6156                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
6157                                 compose->encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
6158                                 g_slist_free(tmp_list);
6159                         } else {
6160                                 compose->encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
6161                         }
6162                         if (compose->encdata != NULL) {
6163                                 if (strcmp(compose->encdata, "_DONT_ENCRYPT_")) {
6164                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
6165                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
6166                                                 compose->encdata) < 0);
6167                                 } /* else we finally dont want to encrypt */
6168                         } else {
6169                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
6170                                 /* and if encdata was null, it means there's been a problem in 
6171                                  * key selection */
6172                                 if (err == TRUE)
6173                                         g_warning("failed to write queue message");
6174                                 fclose(fp);
6175                                 claws_unlink(tmp);
6176                                 g_free(tmp);
6177                                 return -5;
6178                         }
6179                 }
6180         }
6181
6182         /* Save copy folder */
6183         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
6184                 gchar *savefolderid;
6185                 
6186                 savefolderid = compose_get_save_to(compose);
6187                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
6188                 g_free(savefolderid);
6189         }
6190         /* Save copy folder */
6191         if (compose->return_receipt) {
6192                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
6193         }
6194         /* Message-ID of message replying to */
6195         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
6196                 gchar *folderid = NULL;
6197
6198                 if (compose->replyinfo->folder)
6199                         folderid = folder_item_get_identifier(compose->replyinfo->folder);
6200                 if (folderid == NULL)
6201                         folderid = g_strdup("NULL");
6202
6203                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
6204                 g_free(folderid);
6205         }
6206         /* Message-ID of message forwarding to */
6207         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
6208                 gchar *folderid = NULL;
6209                 
6210                 if (compose->fwdinfo->folder)
6211                         folderid = folder_item_get_identifier(compose->fwdinfo->folder);
6212                 if (folderid == NULL)
6213                         folderid = g_strdup("NULL");
6214
6215                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
6216                 g_free(folderid);
6217         }
6218
6219         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
6220         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
6221
6222         /* end of headers */
6223         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
6224
6225         if (compose->redirect_filename != NULL) {
6226                 if (compose_redirect_write_to_file(compose, fp) < 0) {
6227                         fclose(fp);
6228                         claws_unlink(tmp);
6229                         g_free(tmp);
6230                         return -2;
6231                 }
6232         } else {
6233                 gint result = 0;
6234                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
6235                         fclose(fp);
6236                         claws_unlink(tmp);
6237                         g_free(tmp);
6238                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
6239                 }
6240         }
6241         if (err == TRUE) {
6242                 g_warning("failed to write queue message");
6243                 fclose(fp);
6244                 claws_unlink(tmp);
6245                 g_free(tmp);
6246                 return -2;
6247         }
6248         if (fclose(fp) == EOF) {
6249                 FILE_OP_ERROR(tmp, "fclose");
6250                 claws_unlink(tmp);
6251                 g_free(tmp);
6252                 return -2;
6253         }
6254
6255         if (item && *item) {
6256                 queue = *item;
6257         } else {
6258                 queue = account_get_special_folder(compose->account, F_QUEUE);
6259         }
6260         if (!queue) {
6261                 g_warning("can't find queue folder");
6262                 claws_unlink(tmp);
6263                 g_free(tmp);
6264                 return -1;
6265         }
6266         folder_item_scan(queue);
6267         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
6268                 g_warning("can't queue the message");
6269                 claws_unlink(tmp);
6270                 g_free(tmp);
6271                 return -1;
6272         }
6273         
6274         if (msgpath == NULL) {
6275                 claws_unlink(tmp);
6276                 g_free(tmp);
6277         } else
6278                 *msgpath = tmp;
6279
6280         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
6281                 compose_remove_reedit_target(compose, FALSE);
6282         }
6283
6284         if ((msgnum != NULL) && (item != NULL)) {
6285                 *msgnum = num;
6286                 *item = queue;
6287         }
6288
6289         return 0;
6290 }
6291
6292 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6293 {
6294         AttachInfo *ainfo;
6295         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6296         MimeInfo *mimepart;
6297         GStatBuf statbuf;
6298         gchar *type, *subtype;
6299         GtkTreeModel *model;
6300         GtkTreeIter iter;
6301
6302         model = gtk_tree_view_get_model(tree_view);
6303         
6304         if (!gtk_tree_model_get_iter_first(model, &iter))
6305                 return 0;
6306         do {
6307                 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
6308                 
6309                 if (!is_file_exist(ainfo->file)) {
6310                         gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6311                         AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6312                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6313                         g_free(msg);
6314                         if (val == G_ALERTDEFAULT) {
6315                                 return -1;
6316                         }
6317                         continue;
6318                 }
6319                 if (g_stat(ainfo->file, &statbuf) < 0)
6320                         return -1;
6321
6322                 mimepart = procmime_mimeinfo_new();
6323                 mimepart->content = MIMECONTENT_FILE;
6324                 mimepart->data.filename = g_strdup(ainfo->file);
6325                 mimepart->tmp = FALSE; /* or we destroy our attachment */
6326                 mimepart->offset = 0;
6327                 mimepart->length = statbuf.st_size;
6328
6329                 type = g_strdup(ainfo->content_type);
6330
6331                 if (!strchr(type, '/')) {
6332                         g_free(type);
6333                         type = g_strdup("application/octet-stream");
6334                 }
6335
6336                 subtype = strchr(type, '/') + 1;
6337                 *(subtype - 1) = '\0';
6338                 mimepart->type = procmime_get_media_type(type);
6339                 mimepart->subtype = g_strdup(subtype);
6340                 g_free(type);
6341
6342                 if (mimepart->type == MIMETYPE_MESSAGE && 
6343                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6344                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
6345                 } else if (mimepart->type == MIMETYPE_TEXT) {
6346                         if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) {
6347                                 /* Text parts with no name come from multipart/alternative
6348                                 * forwards. Make sure the recipient won't look at the 
6349                                 * original HTML part by mistake. */
6350                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6351                                 ainfo->name = g_strdup_printf(_("Original %s part"),
6352                                                                 mimepart->subtype);
6353                         }
6354                         if (ainfo->charset)
6355                                 g_hash_table_insert(mimepart->typeparameters,
6356                                                     g_strdup("charset"), g_strdup(ainfo->charset));
6357                 }
6358                 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6359                         if (mimepart->type == MIMETYPE_APPLICATION && 
6360                            !strcmp2(mimepart->subtype, "octet-stream"))
6361                                 g_hash_table_insert(mimepart->typeparameters,
6362                                                 g_strdup("name"), g_strdup(ainfo->name));
6363                         g_hash_table_insert(mimepart->dispositionparameters,
6364                                         g_strdup("filename"), g_strdup(ainfo->name));
6365                         mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6366                 }
6367
6368                 if (mimepart->type == MIMETYPE_MESSAGE
6369                     || mimepart->type == MIMETYPE_MULTIPART)
6370                         ainfo->encoding = ENC_BINARY;
6371                 else if (compose->use_signing) {
6372                         if (ainfo->encoding == ENC_7BIT)
6373                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6374                         else if (ainfo->encoding == ENC_8BIT)
6375                                 ainfo->encoding = ENC_BASE64;
6376                 }
6377
6378                 procmime_encode_content(mimepart, ainfo->encoding);
6379
6380                 g_node_append(parent->node, mimepart->node);
6381         } while (gtk_tree_model_iter_next(model, &iter));
6382         
6383         return 0;
6384 }
6385
6386 static gchar *compose_quote_list_of_addresses(gchar *str)
6387 {
6388         GSList *list = NULL, *item = NULL;
6389         gchar *qname = NULL, *faddr = NULL, *result = NULL;
6390
6391         list = address_list_append_with_comments(list, str);
6392         for (item = list; item != NULL; item = item->next) {
6393                 gchar *spec = item->data;
6394                 gchar *endofname = strstr(spec, " <");
6395                 if (endofname != NULL) {
6396                         gchar * qqname;
6397                         *endofname = '\0';
6398                         QUOTE_IF_REQUIRED_NORMAL(qname, spec, return NULL);
6399                         qqname = escape_internal_quotes(qname, '"');
6400                         *endofname = ' ';
6401                         if (*qname != *spec || qqname != qname) { /* has been quoted, compute new */
6402                                 gchar *addr = g_strdup(endofname);
6403                                 gchar *name = (qqname != qname)? qqname: g_strdup(qname);
6404                                 faddr = g_strconcat(name, addr, NULL);
6405                                 g_free(name);
6406                                 g_free(addr);
6407                                 debug_print("new auto-quoted address: '%s'\n", faddr);
6408                         }
6409                 }
6410                 if (result == NULL)
6411                         result = g_strdup((faddr != NULL)? faddr: spec);
6412                 else {
6413                         result = g_strconcat(result,
6414                                              ", ",
6415                                              (faddr != NULL)? faddr: spec,
6416                                              NULL);
6417                 }
6418                 if (faddr != NULL) {
6419                         g_free(faddr);
6420                         faddr = NULL;
6421                 }
6422         }
6423         slist_free_strings_full(list);
6424
6425         return result;
6426 }
6427
6428 #define IS_IN_CUSTOM_HEADER(header) \
6429         (compose->account->add_customhdr && \
6430          custom_header_find(compose->account->customhdr_list, header) != NULL)
6431
6432 static const gchar *compose_untranslated_header_name(gchar *header_name)
6433 {
6434         /* return the untranslated header name, if header_name is a known
6435            header name, in either its translated or untranslated form, with
6436            or without trailing colon. otherwise, returns header_name. */
6437         gchar *translated_header_name;
6438         gchar *translated_header_name_wcolon;
6439         const gchar *untranslated_header_name;
6440         const gchar *untranslated_header_name_wcolon;
6441         gint i;
6442
6443         cm_return_val_if_fail(header_name != NULL, NULL);
6444
6445         for (i = 0; HEADERS[i].header_name != NULL; i++) {
6446                 untranslated_header_name = HEADERS[i].header_name;
6447                 untranslated_header_name_wcolon = HEADERS[i].header_name_w_colon;
6448
6449                 translated_header_name = gettext(untranslated_header_name);
6450                 translated_header_name_wcolon = gettext(untranslated_header_name_wcolon);
6451
6452                 if (!strcmp(header_name, untranslated_header_name) ||
6453                         !strcmp(header_name, translated_header_name)) {
6454                         return untranslated_header_name;
6455                 } else {
6456                         if (!strcmp(header_name, untranslated_header_name_wcolon) ||
6457                                 !strcmp(header_name, translated_header_name_wcolon)) {
6458                                 return untranslated_header_name_wcolon;
6459                         }
6460                 }
6461         }
6462         debug_print("compose_untranslated_header_name: unknown header '%s'\n", header_name);
6463         return header_name;
6464 }
6465
6466 static void compose_add_headerfield_from_headerlist(Compose *compose, 
6467                                                     GString *header, 
6468                                                     const gchar *fieldname,
6469                                                     const gchar *seperator)
6470 {
6471         gchar *str, *fieldname_w_colon;
6472         gboolean add_field = FALSE;
6473         GSList *list;
6474         ComposeHeaderEntry *headerentry;
6475         const gchar *headerentryname;
6476         const gchar *trans_fieldname;
6477         GString *fieldstr;
6478
6479         if (IS_IN_CUSTOM_HEADER(fieldname))
6480                 return;
6481
6482         debug_print("Adding %s-fields\n", fieldname);
6483
6484         fieldstr = g_string_sized_new(64);
6485
6486         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6487         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6488
6489         for (list = compose->header_list; list; list = list->next) {
6490                 headerentry = ((ComposeHeaderEntry *)list->data);
6491                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6492
6493                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6494                         gchar * ustr = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6495                         g_strstrip(ustr);
6496                         str = compose_quote_list_of_addresses(ustr);
6497                         g_free(ustr);
6498                         if (str != NULL && str[0] != '\0') {
6499                                 if (add_field)
6500                                         g_string_append(fieldstr, seperator);
6501                                 g_string_append(fieldstr, str);
6502                                 add_field = TRUE;
6503                         }
6504                         g_free(str);
6505                 }
6506         }
6507         if (add_field) {
6508                 gchar *buf;
6509
6510                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6511                 compose_convert_header
6512                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
6513                         strlen(fieldname) + 2, TRUE);
6514                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6515                 g_free(buf);
6516         }
6517
6518         g_free(fieldname_w_colon);
6519         g_string_free(fieldstr, TRUE);
6520
6521         return;
6522 }
6523
6524 static gchar *compose_get_manual_headers_info(Compose *compose)
6525 {
6526         GString *sh_header = g_string_new(" ");
6527         GSList *list;
6528         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6529
6530         for (list = compose->header_list; list; list = list->next) {
6531                 ComposeHeaderEntry *headerentry;
6532                 gchar *tmp;
6533                 gchar *headername;
6534                 gchar *headername_wcolon;
6535                 const gchar *headername_trans;
6536                 gchar **string;
6537                 gboolean standard_header = FALSE;
6538
6539                 headerentry = ((ComposeHeaderEntry *)list->data);
6540
6541                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6542                 g_strstrip(tmp);
6543                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6544                         g_free(tmp);
6545                         continue;
6546                 }
6547
6548                 if (!strstr(tmp, ":")) {
6549                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6550                         headername = g_strdup(tmp);
6551                 } else {
6552                         headername_wcolon = g_strdup(tmp);
6553                         headername = g_strdup(strtok(tmp, ":"));
6554                 }
6555                 g_free(tmp);
6556                 
6557                 string = std_headers;
6558                 while (*string != NULL) {
6559                         headername_trans = prefs_common_translated_header_name(*string);
6560                         if (!strcmp(headername_trans, headername_wcolon))
6561                                 standard_header = TRUE;
6562                         string++;
6563                 }
6564                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6565                         g_string_append_printf(sh_header, "%s ", headername);
6566                 g_free(headername);
6567                 g_free(headername_wcolon);
6568         }
6569         g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6570         return g_string_free(sh_header, FALSE);
6571 }
6572
6573 static gchar *compose_get_header(Compose *compose)
6574 {
6575         gchar date[RFC822_DATE_BUFFSIZE];
6576         gchar buf[BUFFSIZE];
6577         const gchar *entry_str;
6578         gchar *str;
6579         gchar *name;
6580         GSList *list;
6581         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6582         GString *header;
6583         gchar *from_name = NULL, *from_address = NULL;
6584         gchar *tmp;
6585
6586         cm_return_val_if_fail(compose->account != NULL, NULL);
6587         cm_return_val_if_fail(compose->account->address != NULL, NULL);
6588
6589         header = g_string_sized_new(64);
6590
6591         /* Date */
6592         if (prefs_common.hide_timezone)
6593                 get_rfc822_date_hide_tz(date, sizeof(date));
6594         else
6595                 get_rfc822_date(date, sizeof(date));
6596         g_string_append_printf(header, "Date: %s\n", date);
6597
6598         /* From */
6599         
6600         if (compose->account->name && *compose->account->name) {
6601                 gchar *buf;
6602                 QUOTE_IF_REQUIRED(buf, compose->account->name);
6603                 tmp = g_strdup_printf("%s <%s>",
6604                         buf, compose->account->address);
6605         } else {
6606                 tmp = g_strdup_printf("%s",
6607                         compose->account->address);
6608         }
6609         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6610         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6611                 /* use default */
6612                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6613                 from_address = g_strdup(compose->account->address);
6614         } else {
6615                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6616                 /* extract name and address */
6617                 if (strstr(spec, " <") && strstr(spec, ">")) {
6618                         from_address = g_strdup(strrchr(spec, '<')+1);
6619                         *(strrchr(from_address, '>')) = '\0';
6620                         from_name = g_strdup(spec);
6621                         *(strrchr(from_name, '<')) = '\0';
6622                 } else {
6623                         from_name = NULL;
6624                         from_address = g_strdup(spec);
6625                 }
6626                 g_free(spec);
6627         }
6628         g_free(tmp);
6629         
6630         
6631         if (from_name && *from_name) {
6632                 gchar *qname;
6633                 compose_convert_header
6634                         (compose, buf, sizeof(buf), from_name,
6635                          strlen("From: "), TRUE);
6636                 QUOTE_IF_REQUIRED(name, buf);
6637                 qname = escape_internal_quotes(name, '"');
6638                 
6639                 g_string_append_printf(header, "From: %s <%s>\n",
6640                         qname, from_address);
6641                 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6642                     compose->return_receipt) {
6643                         compose_convert_header(compose, buf, sizeof(buf), from_name,
6644                                                strlen("Disposition-Notification-To: "),
6645                                                TRUE);
6646                         g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, from_address);
6647                 }
6648                 if (qname != name)
6649                         g_free(qname);
6650         } else {
6651                 g_string_append_printf(header, "From: %s\n", from_address);
6652                 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6653                     compose->return_receipt)
6654                         g_string_append_printf(header, "Disposition-Notification-To: %s\n", from_address);
6655
6656         }
6657         g_free(from_name);
6658         g_free(from_address);
6659
6660         /* To */
6661         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6662
6663         /* Newsgroups */
6664         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6665
6666         /* Cc */
6667         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6668
6669         /* Bcc */
6670         /* 
6671          * If this account is a NNTP account remove Bcc header from 
6672          * message body since it otherwise will be publicly shown
6673          */
6674         if (compose->account->protocol != A_NNTP)
6675                 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6676
6677         /* Subject */
6678         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6679
6680         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6681                 g_strstrip(str);
6682                 if (*str != '\0') {
6683                         compose_convert_header(compose, buf, sizeof(buf), str,
6684                                                strlen("Subject: "), FALSE);
6685                         g_string_append_printf(header, "Subject: %s\n", buf);
6686                 }
6687         }
6688         g_free(str);
6689
6690         /* Message-ID */
6691         if (compose->msgid != NULL && strlen(compose->msgid) > 0) {
6692                 g_string_append_printf(header, "Message-ID: <%s>\n",
6693                                 compose->msgid);
6694         }
6695
6696         if (compose->remove_references == FALSE) {
6697                 /* In-Reply-To */
6698                 if (compose->inreplyto && compose->to_list)
6699                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6700         
6701                 /* References */
6702                 if (compose->references)
6703                         g_string_append_printf(header, "References: %s\n", compose->references);
6704         }
6705
6706         /* Followup-To */
6707         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6708
6709         /* Reply-To */
6710         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6711
6712         /* Organization */
6713         if (compose->account->organization &&
6714             strlen(compose->account->organization) &&
6715             !IS_IN_CUSTOM_HEADER("Organization")) {
6716                 compose_convert_header(compose, buf, sizeof(buf),
6717                                        compose->account->organization,
6718                                        strlen("Organization: "), FALSE);
6719                 g_string_append_printf(header, "Organization: %s\n", buf);
6720         }
6721
6722         /* Program version and system info */
6723         if (compose->account->gen_xmailer &&
6724             g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6725             !compose->newsgroup_list) {
6726                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6727                         prog_version,
6728                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6729                         TARGET_ALIAS);
6730         }
6731         if (compose->account->gen_xmailer &&
6732             g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6733                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6734                         prog_version,
6735                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6736                         TARGET_ALIAS);
6737         }
6738
6739         /* custom headers */
6740         if (compose->account->add_customhdr) {
6741                 GSList *cur;
6742
6743                 for (cur = compose->account->customhdr_list; cur != NULL;
6744                      cur = cur->next) {
6745                         CustomHeader *chdr = (CustomHeader *)cur->data;
6746
6747                         if (custom_header_is_allowed(chdr->name)
6748                             && chdr->value != NULL
6749                             && *(chdr->value) != '\0') {
6750                                 compose_convert_header
6751                                         (compose, buf, sizeof(buf),
6752                                          chdr->value,
6753                                          strlen(chdr->name) + 2, FALSE);
6754                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6755                         }
6756                 }
6757         }
6758
6759         /* Automatic Faces and X-Faces */
6760         if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6761                 g_string_append_printf(header, "X-Face: %s\n", buf);
6762         }
6763         else if (get_default_xface (buf, sizeof(buf)) == 0) {
6764                 g_string_append_printf(header, "X-Face: %s\n", buf);
6765         }
6766         if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6767                 g_string_append_printf(header, "Face: %s\n", buf);
6768         }
6769         else if (get_default_face (buf, sizeof(buf)) == 0) {
6770                 g_string_append_printf(header, "Face: %s\n", buf);
6771         }
6772
6773         /* PRIORITY */
6774         switch (compose->priority) {
6775                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6776                                                    "X-Priority: 1 (Highest)\n");
6777                         break;
6778                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6779                                                 "X-Priority: 2 (High)\n");
6780                         break;
6781                 case PRIORITY_NORMAL: break;
6782                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6783                                                "X-Priority: 4 (Low)\n");
6784                         break;
6785                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6786                                                   "X-Priority: 5 (Lowest)\n");
6787                         break;
6788                 default: debug_print("compose: priority unknown : %d\n",
6789                                      compose->priority);
6790         }
6791
6792         /* get special headers */
6793         for (list = compose->header_list; list; list = list->next) {
6794                 ComposeHeaderEntry *headerentry;
6795                 gchar *tmp;
6796                 gchar *headername;
6797                 gchar *headername_wcolon;
6798                 const gchar *headername_trans;
6799                 gchar *headervalue;
6800                 gchar **string;
6801                 gboolean standard_header = FALSE;
6802
6803                 headerentry = ((ComposeHeaderEntry *)list->data);
6804
6805                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6806                 g_strstrip(tmp);
6807                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6808                         g_free(tmp);
6809                         continue;
6810                 }
6811
6812                 if (!strstr(tmp, ":")) {
6813                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6814                         headername = g_strdup(tmp);
6815                 } else {
6816                         headername_wcolon = g_strdup(tmp);
6817                         headername = g_strdup(strtok(tmp, ":"));
6818                 }
6819                 g_free(tmp);
6820                 
6821                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6822                 Xstrdup_a(headervalue, entry_str, return NULL);
6823                 subst_char(headervalue, '\r', ' ');
6824                 subst_char(headervalue, '\n', ' ');
6825                 g_strstrip(headervalue);
6826                 if (*headervalue != '\0') {
6827                         string = std_headers;
6828                         while (*string != NULL && !standard_header) {
6829                                 headername_trans = prefs_common_translated_header_name(*string);
6830                                 /* support mixed translated and untranslated headers */
6831                                 if (!strcmp(headername_trans, headername_wcolon) || !strcmp(*string, headername_wcolon))
6832                                         standard_header = TRUE;
6833                                 string++;
6834                         }
6835                         if (!standard_header && !IS_IN_CUSTOM_HEADER(headername)) {
6836                                 /* store untranslated header name */
6837                                 g_string_append_printf(header, "%s %s\n",
6838                                                 compose_untranslated_header_name(headername_wcolon), headervalue);
6839                         }
6840                 }                               
6841                 g_free(headername);
6842                 g_free(headername_wcolon);              
6843         }
6844
6845         str = header->str;
6846         g_string_free(header, FALSE);
6847
6848         return str;
6849 }
6850
6851 #undef IS_IN_CUSTOM_HEADER
6852
6853 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6854                                    gint header_len, gboolean addr_field)
6855 {
6856         gchar *tmpstr = NULL;
6857         const gchar *out_codeset = NULL;
6858
6859         cm_return_if_fail(src != NULL);
6860         cm_return_if_fail(dest != NULL);
6861
6862         if (len < 1) return;
6863
6864         tmpstr = g_strdup(src);
6865
6866         subst_char(tmpstr, '\n', ' ');
6867         subst_char(tmpstr, '\r', ' ');
6868         g_strchomp(tmpstr);
6869
6870         if (!g_utf8_validate(tmpstr, -1, NULL)) {
6871                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6872                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6873                 g_free(tmpstr);
6874                 tmpstr = mybuf;
6875         }
6876
6877         codeconv_set_strict(TRUE);
6878         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6879                 conv_get_charset_str(compose->out_encoding));
6880         codeconv_set_strict(FALSE);
6881         
6882         if (!dest || *dest == '\0') {
6883                 gchar *test_conv_global_out = NULL;
6884                 gchar *test_conv_reply = NULL;
6885
6886                 /* automatic mode. be automatic. */
6887                 codeconv_set_strict(TRUE);
6888
6889                 out_codeset = conv_get_outgoing_charset_str();
6890                 if (out_codeset) {
6891                         debug_print("trying to convert to %s\n", out_codeset);
6892                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6893                 }
6894
6895                 if (!test_conv_global_out && compose->orig_charset
6896                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
6897                         out_codeset = compose->orig_charset;
6898                         debug_print("failure; trying to convert to %s\n", out_codeset);
6899                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6900                 }
6901
6902                 if (!test_conv_global_out && !test_conv_reply) {
6903                         /* we're lost */
6904                         out_codeset = CS_INTERNAL;
6905                         debug_print("finally using %s\n", out_codeset);
6906                 }
6907                 g_free(test_conv_global_out);
6908                 g_free(test_conv_reply);
6909                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6910                                         out_codeset);
6911                 codeconv_set_strict(FALSE);
6912         }
6913         g_free(tmpstr);
6914 }
6915
6916 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6917 {
6918         gchar *address;
6919
6920         cm_return_if_fail(user_data != NULL);
6921
6922         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6923         g_strstrip(address);
6924         if (*address != '\0') {
6925                 gchar *name = procheader_get_fromname(address);
6926                 extract_address(address);
6927 #ifndef USE_ALT_ADDRBOOK
6928                 addressbook_add_contact(name, address, NULL, NULL);
6929 #else
6930                 debug_print("%s: %s\n", name, address);
6931                 if (addressadd_selection(name, address, NULL, NULL)) {
6932                         debug_print( "addressbook_add_contact - added\n" );
6933                 }
6934 #endif
6935         }
6936         g_free(address);
6937 }
6938
6939 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6940 {
6941         GtkWidget *menuitem;
6942         gchar *address;
6943
6944         cm_return_if_fail(menu != NULL);
6945         cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6946
6947         menuitem = gtk_separator_menu_item_new();
6948         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6949         gtk_widget_show(menuitem);
6950
6951         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6952         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6953
6954         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6955         g_strstrip(address);
6956         if (*address == '\0') {
6957                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6958         }
6959
6960         g_signal_connect(G_OBJECT(menuitem), "activate",
6961                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
6962         gtk_widget_show(menuitem);
6963 }
6964
6965 void compose_add_extra_header(gchar *header, GtkListStore *model)
6966 {
6967         GtkTreeIter iter;
6968         if (strcmp(header, "")) {
6969                 COMBOBOX_ADD(model, header, COMPOSE_TO);
6970         }
6971 }
6972
6973 void compose_add_extra_header_entries(GtkListStore *model)
6974 {
6975         FILE *exh;
6976         gchar *exhrc;
6977         gchar buf[BUFFSIZE];
6978         gint lastc;
6979
6980         if (extra_headers == NULL) {
6981                 exhrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "extraheaderrc", NULL);
6982                 if ((exh = g_fopen(exhrc, "rb")) == NULL) {
6983                         debug_print("extra headers file not found\n");
6984                         goto extra_headers_done;
6985                 }
6986                 while (fgets(buf, BUFFSIZE, exh) != NULL) {
6987                         lastc = strlen(buf) - 1;        /* remove trailing control chars */
6988                         while (lastc >= 0 && buf[lastc] != ':')
6989                                 buf[lastc--] = '\0';
6990                         if (lastc > 0 && buf[0] != '#' && buf[lastc] == ':') {
6991                                 buf[lastc] = '\0'; /* remove trailing : for comparison */
6992                                 if (custom_header_is_allowed(buf)) {
6993                                         buf[lastc] = ':';
6994                                         extra_headers = g_slist_prepend(extra_headers, g_strdup(buf));
6995                                 }
6996                                 else
6997                                         g_message("disallowed extra header line: %s\n", buf);
6998                         }
6999                         else {
7000                                 if (buf[0] != '#')
7001                                         g_message("invalid extra header line: %s\n", buf);
7002                         }
7003                 }
7004                 fclose(exh);
7005 extra_headers_done:
7006                 g_free(exhrc);
7007                 extra_headers = g_slist_prepend(extra_headers, g_strdup("")); /* end of list */
7008                 extra_headers = g_slist_reverse(extra_headers);
7009         }
7010         g_slist_foreach(extra_headers, (GFunc)compose_add_extra_header, (gpointer)model);
7011 }
7012
7013 static void compose_create_header_entry(Compose *compose) 
7014 {
7015         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
7016
7017         GtkWidget *combo;
7018         GtkWidget *entry;
7019         GtkWidget *button;
7020         GtkWidget *hbox;
7021         gchar **string;
7022         const gchar *header = NULL;
7023         ComposeHeaderEntry *headerentry;
7024         gboolean standard_header = FALSE;
7025         GtkListStore *model;
7026         GtkTreeIter iter;
7027         
7028         headerentry = g_new0(ComposeHeaderEntry, 1);
7029
7030         /* Combo box model */
7031         model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
7032 #if !GTK_CHECK_VERSION(2, 24, 0)
7033         combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
7034 #endif
7035         COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
7036                         COMPOSE_TO);
7037         COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
7038                         COMPOSE_CC);
7039         COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
7040                         COMPOSE_BCC);
7041         COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
7042                         COMPOSE_NEWSGROUPS);                    
7043         COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
7044                         COMPOSE_REPLYTO);
7045         COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
7046                         COMPOSE_FOLLOWUPTO);
7047         compose_add_extra_header_entries(model);
7048
7049         /* Combo box */
7050 #if GTK_CHECK_VERSION(2, 24, 0)
7051         combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model));
7052         GtkCellRenderer *cell = gtk_cell_renderer_text_new();
7053         gtk_cell_renderer_set_alignment(cell, 0.0, 0.5);
7054         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
7055         gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo), 0);
7056 #endif
7057         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
7058         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
7059                          G_CALLBACK(compose_grab_focus_cb), compose);
7060         gtk_widget_show(combo);
7061
7062         /* Putting only the combobox child into focus chain of its parent causes
7063          * the parent to be skipped when changing focus via Tab or Shift+Tab.
7064          * This eliminates need to pres Tab twice in order to really get from the
7065          * combobox to next widget. */
7066         GList *l = NULL;
7067         l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
7068         gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
7069         g_list_free(l);
7070
7071         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
7072                         compose->header_nextrow, compose->header_nextrow+1,
7073                         GTK_SHRINK, GTK_FILL, 0, 0);
7074         if (compose->header_last && (compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN)) {
7075                 const gchar *last_header_entry = gtk_entry_get_text(
7076                                 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
7077                 string = headers;
7078                 while (*string != NULL) {
7079                         if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
7080                                 standard_header = TRUE;
7081                         string++;
7082                 }
7083                 if (standard_header)
7084                         header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
7085         }
7086         if (!compose->header_last || !standard_header) {
7087                 switch(compose->account->protocol) {
7088                         case A_NNTP:
7089                                 header = prefs_common_translated_header_name("Newsgroups:");
7090                                 break;
7091                         default:
7092                                 header = prefs_common_translated_header_name("To:");
7093                                 break;
7094                 }                                                                   
7095         }
7096         if (header)
7097                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
7098
7099         gtk_editable_set_editable(
7100                 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo)))),
7101                 prefs_common.type_any_header);
7102
7103         g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
7104                          G_CALLBACK(compose_grab_focus_cb), compose);
7105
7106         /* Entry field with cleanup button */
7107         button = gtk_button_new();
7108         gtk_button_set_image(GTK_BUTTON(button),
7109                         gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
7110         gtk_widget_show(button);
7111         CLAWS_SET_TIP(button,
7112                 _("Delete entry contents"));
7113         entry = gtk_entry_new(); 
7114         gtk_widget_show(entry);
7115         CLAWS_SET_TIP(entry,
7116                 _("Use <tab> to autocomplete from addressbook"));
7117         hbox = gtk_hbox_new (FALSE, 0);
7118         gtk_widget_show(hbox);
7119         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
7120         gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
7121         gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
7122                         compose->header_nextrow, compose->header_nextrow+1,
7123                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
7124
7125         g_signal_connect(G_OBJECT(entry), "key-press-event", 
7126                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
7127                          headerentry);
7128         g_signal_connect(G_OBJECT(entry), "changed", 
7129                          G_CALLBACK(compose_headerentry_changed_cb), 
7130                          headerentry);
7131         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
7132                          G_CALLBACK(compose_grab_focus_cb), compose);
7133
7134         g_signal_connect(G_OBJECT(button), "clicked",
7135                          G_CALLBACK(compose_headerentry_button_clicked_cb),
7136                          headerentry); 
7137                          
7138         /* email dnd */
7139         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7140                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7141                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7142         g_signal_connect(G_OBJECT(entry), "drag_data_received",
7143                          G_CALLBACK(compose_header_drag_received_cb),
7144                          entry);
7145         g_signal_connect(G_OBJECT(entry), "drag-drop",
7146                          G_CALLBACK(compose_drag_drop),
7147                          compose);
7148         g_signal_connect(G_OBJECT(entry), "populate-popup",
7149                          G_CALLBACK(compose_entry_popup_extend),
7150                          NULL);
7151         
7152         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
7153
7154         headerentry->compose = compose;
7155         headerentry->combo = combo;
7156         headerentry->entry = entry;
7157         headerentry->button = button;
7158         headerentry->hbox = hbox;
7159         headerentry->headernum = compose->header_nextrow;
7160         headerentry->type = PREF_NONE;
7161
7162         compose->header_nextrow++;
7163         compose->header_last = headerentry;             
7164         compose->header_list =
7165                 g_slist_append(compose->header_list,
7166                                headerentry);
7167 }
7168
7169 static void compose_add_header_entry(Compose *compose, const gchar *header,
7170                                 gchar *text, ComposePrefType pref_type) 
7171 {
7172         ComposeHeaderEntry *last_header = compose->header_last;
7173         gchar *tmp = g_strdup(text), *email;
7174         gboolean replyto_hdr;
7175         
7176         replyto_hdr = (!strcasecmp(header,
7177                                 prefs_common_translated_header_name("Reply-To:")) ||
7178                         !strcasecmp(header,
7179                                 prefs_common_translated_header_name("Followup-To:")) ||
7180                         !strcasecmp(header,
7181                                 prefs_common_translated_header_name("In-Reply-To:")));
7182                 
7183         extract_address(tmp);
7184         email = g_utf8_strdown(tmp, -1);
7185         
7186         if (replyto_hdr == FALSE &&
7187             g_hash_table_lookup(compose->email_hashtable, email) != NULL)
7188         {
7189                 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7190                                 header, text, (gint) pref_type);
7191                 g_free(email);
7192                 g_free(tmp);
7193                 return;
7194         }
7195         
7196         if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
7197                 gtk_entry_set_text(GTK_ENTRY(
7198                         gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
7199         else
7200                 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
7201         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
7202         last_header->type = pref_type;
7203
7204         if (replyto_hdr == FALSE)
7205                 g_hash_table_insert(compose->email_hashtable, email,
7206                                     GUINT_TO_POINTER(1));
7207         else
7208                 g_free(email);
7209         
7210         g_free(tmp);
7211 }
7212
7213 static void compose_destroy_headerentry(Compose *compose, 
7214                                         ComposeHeaderEntry *headerentry)
7215 {
7216         gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
7217         gchar *email;
7218
7219         extract_address(text);
7220         email = g_utf8_strdown(text, -1);
7221         g_hash_table_remove(compose->email_hashtable, email);
7222         g_free(text);
7223         g_free(email);
7224         
7225         gtk_widget_destroy(headerentry->combo);
7226         gtk_widget_destroy(headerentry->entry);
7227         gtk_widget_destroy(headerentry->button);
7228         gtk_widget_destroy(headerentry->hbox);
7229         g_free(headerentry);
7230 }
7231
7232 static void compose_remove_header_entries(Compose *compose) 
7233 {
7234         GSList *list;
7235         for (list = compose->header_list; list; list = list->next)
7236                 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
7237
7238         compose->header_last = NULL;
7239         g_slist_free(compose->header_list);
7240         compose->header_list = NULL;
7241         compose->header_nextrow = 1;
7242         compose_create_header_entry(compose);
7243 }
7244
7245 static GtkWidget *compose_create_header(Compose *compose) 
7246 {
7247         GtkWidget *from_optmenu_hbox;
7248         GtkWidget *header_table_main;
7249         GtkWidget *header_scrolledwin;
7250         GtkWidget *header_table;
7251
7252         /* parent with account selection and from header */
7253         header_table_main = gtk_table_new(2, 2, FALSE);
7254         gtk_widget_show(header_table_main);
7255         gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
7256
7257         from_optmenu_hbox = compose_account_option_menu_create(compose);
7258         gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
7259                                   0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
7260
7261         /* child with header labels and entries */
7262         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7263         gtk_widget_show(header_scrolledwin);
7264         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
7265
7266         header_table = gtk_table_new(2, 2, FALSE);
7267         gtk_widget_show(header_table);
7268         gtk_container_set_border_width(GTK_CONTAINER(header_table), 0);
7269         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
7270         gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table),
7271                         gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin)));
7272         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin))), GTK_SHADOW_NONE);
7273
7274         gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
7275                                   0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
7276
7277         compose->header_table = header_table;
7278         compose->header_list = NULL;
7279         compose->header_nextrow = 0;
7280
7281         compose_create_header_entry(compose);
7282
7283         compose->table = NULL;
7284
7285         return header_table_main;
7286 }
7287
7288 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
7289 {
7290         Compose *compose = (Compose *)data;
7291         GdkEventButton event;
7292         
7293         event.button = 3;
7294         event.time = gtk_get_current_event_time();
7295
7296         return attach_button_pressed(compose->attach_clist, &event, compose);
7297 }
7298
7299 static GtkWidget *compose_create_attach(Compose *compose)
7300 {
7301         GtkWidget *attach_scrwin;
7302         GtkWidget *attach_clist;
7303
7304         GtkListStore *store;
7305         GtkCellRenderer *renderer;
7306         GtkTreeViewColumn *column;
7307         GtkTreeSelection *selection;
7308
7309         /* attachment list */
7310         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
7311         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
7312                                        GTK_POLICY_AUTOMATIC,
7313                                        GTK_POLICY_AUTOMATIC);
7314         gtk_widget_set_size_request(attach_scrwin, -1, 80);
7315
7316         store = gtk_list_store_new(N_ATTACH_COLS, 
7317                                    G_TYPE_STRING,
7318                                    G_TYPE_STRING,
7319                                    G_TYPE_STRING,
7320                                    G_TYPE_STRING,
7321                                    G_TYPE_POINTER,
7322                                    G_TYPE_AUTO_POINTER,
7323                                    -1);
7324         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
7325                                         (GTK_TREE_MODEL(store)));
7326         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
7327         g_object_unref(store);
7328         
7329         renderer = gtk_cell_renderer_text_new();
7330         column = gtk_tree_view_column_new_with_attributes
7331                         (_("Mime type"), renderer, "text", 
7332                          COL_MIMETYPE, NULL);
7333         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7334         
7335         renderer = gtk_cell_renderer_text_new();
7336         column = gtk_tree_view_column_new_with_attributes
7337                         (_("Size"), renderer, "text", 
7338                          COL_SIZE, NULL);
7339         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7340         
7341         renderer = gtk_cell_renderer_text_new();
7342         column = gtk_tree_view_column_new_with_attributes
7343                         (_("Name"), renderer, "text", 
7344                          COL_NAME, NULL);
7345         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7346
7347         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
7348                                      prefs_common.use_stripes_everywhere);
7349         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
7350         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
7351
7352         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
7353                          G_CALLBACK(attach_selected), compose);
7354         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
7355                          G_CALLBACK(attach_button_pressed), compose);
7356         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
7357                          G_CALLBACK(popup_attach_button_pressed), compose);
7358         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
7359                          G_CALLBACK(attach_key_pressed), compose);
7360
7361         /* drag and drop */
7362         gtk_drag_dest_set(attach_clist,
7363                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7364                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7365                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7366         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
7367                          G_CALLBACK(compose_attach_drag_received_cb),
7368                          compose);
7369         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
7370                          G_CALLBACK(compose_drag_drop),
7371                          compose);
7372
7373         compose->attach_scrwin = attach_scrwin;
7374         compose->attach_clist  = attach_clist;
7375
7376         return attach_scrwin;
7377 }
7378
7379 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
7380 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
7381
7382 static GtkWidget *compose_create_others(Compose *compose)
7383 {
7384         GtkWidget *table;
7385         GtkWidget *savemsg_checkbtn;
7386         GtkWidget *savemsg_combo;
7387         GtkWidget *savemsg_select;
7388         
7389         guint rowcount = 0;
7390         gchar *folderidentifier;
7391
7392         /* Table for settings */
7393         table = gtk_table_new(3, 1, FALSE);
7394         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7395         gtk_widget_show(table);
7396         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7397         rowcount = 0;
7398
7399         /* Save Message to folder */
7400         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7401         gtk_widget_show(savemsg_checkbtn);
7402         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7403         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7404                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7405         }
7406         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7407                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7408
7409 #if !GTK_CHECK_VERSION(2, 24, 0)
7410         savemsg_combo = gtk_combo_box_entry_new_text();
7411 #else
7412         savemsg_combo = gtk_combo_box_text_new_with_entry();
7413 #endif
7414         compose->savemsg_checkbtn = savemsg_checkbtn;
7415         compose->savemsg_combo = savemsg_combo;
7416         gtk_widget_show(savemsg_combo);
7417
7418         if (prefs_common.compose_save_to_history)
7419 #if !GTK_CHECK_VERSION(2, 24, 0)
7420                 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7421                                 prefs_common.compose_save_to_history);
7422 #else
7423                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo),
7424                                 prefs_common.compose_save_to_history);
7425 #endif
7426         gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7427         gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7428         g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7429                          G_CALLBACK(compose_grab_focus_cb), compose);
7430         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7431                 folderidentifier = folder_item_get_identifier(account_get_special_folder
7432                                   (compose->account, F_OUTBOX));
7433                 compose_set_save_to(compose, folderidentifier);
7434                 g_free(folderidentifier);
7435         }
7436
7437         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7438         gtk_widget_show(savemsg_select);
7439         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7440         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7441                          G_CALLBACK(compose_savemsg_select_cb),
7442                          compose);
7443
7444         return table;   
7445 }
7446
7447 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
7448 {
7449         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7450                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7451 }
7452
7453 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7454 {
7455         FolderItem *dest;
7456         gchar * path;
7457
7458         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE,
7459                         _("Select folder to save message to"));
7460         if (!dest) return;
7461
7462         path = folder_item_get_identifier(dest);
7463
7464         compose_set_save_to(compose, path);
7465         g_free(path);
7466 }
7467
7468 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7469                                   GdkAtom clip, GtkTextIter *insert_place);
7470
7471
7472 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7473                                        Compose *compose)
7474 {
7475         gint prev_autowrap;
7476         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7477 #if USE_ENCHANT
7478         if (event->button == 3) {
7479                 GtkTextIter iter;
7480                 GtkTextIter sel_start, sel_end;
7481                 gboolean stuff_selected;
7482                 gint x, y;
7483                 /* move the cursor to allow GtkAspell to check the word
7484                  * under the mouse */
7485                 if (event->x && event->y) {
7486                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7487                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7488                                 &x, &y);
7489                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7490                                 &iter, x, y);
7491                 } else {
7492                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7493                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7494                 }
7495                 /* get selection */
7496                 stuff_selected = gtk_text_buffer_get_selection_bounds(
7497                                 buffer,
7498                                 &sel_start, &sel_end);
7499
7500                 gtk_text_buffer_place_cursor (buffer, &iter);
7501                 /* reselect stuff */
7502                 if (stuff_selected 
7503                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7504                         gtk_text_buffer_select_range(buffer,
7505                                 &sel_start, &sel_end);
7506                 }
7507                 return FALSE; /* pass the event so that the right-click goes through */
7508         }
7509 #endif
7510         if (event->button == 2) {
7511                 GtkTextIter iter;
7512                 gint x, y;
7513                 BLOCK_WRAP();
7514                 
7515                 /* get the middle-click position to paste at the correct place */
7516                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7517                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7518                         &x, &y);
7519                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7520                         &iter, x, y);
7521                 
7522                 entry_paste_clipboard(compose, text, 
7523                                 prefs_common.linewrap_pastes,
7524                                 GDK_SELECTION_PRIMARY, &iter);
7525                 UNBLOCK_WRAP();
7526                 return TRUE;
7527         }
7528         return FALSE;
7529 }
7530
7531 #if USE_ENCHANT
7532 static void compose_spell_menu_changed(void *data)
7533 {
7534         Compose *compose = (Compose *)data;
7535         GSList *items;
7536         GtkWidget *menuitem;
7537         GtkWidget *parent_item;
7538         GtkMenu *menu = GTK_MENU(gtk_menu_new());
7539         GSList *spell_menu;
7540
7541         if (compose->gtkaspell == NULL)
7542                 return;
7543
7544         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
7545                         "/Menu/Spelling/Options");
7546
7547         /* setting the submenu removes /Spelling/Options from the factory 
7548          * so we need to save it */
7549
7550         if (parent_item == NULL) {
7551                 parent_item = compose->aspell_options_menu;
7552                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7553         } else
7554                 compose->aspell_options_menu = parent_item;
7555
7556         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7557
7558         spell_menu = g_slist_reverse(spell_menu);
7559         for (items = spell_menu;
7560              items; items = items->next) {
7561                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7562                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7563                 gtk_widget_show(GTK_WIDGET(menuitem));
7564         }
7565         g_slist_free(spell_menu);
7566
7567         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7568         gtk_widget_show(parent_item);
7569 }
7570
7571 static void compose_dict_changed(void *data)
7572 {
7573         Compose *compose = (Compose *) data;
7574
7575         if(!compose->gtkaspell)
7576                 return; 
7577         if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
7578                 return;
7579
7580         gtkaspell_highlight_all(compose->gtkaspell);
7581         claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7582 }
7583 #endif
7584
7585 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7586 {
7587         Compose *compose = (Compose *)data;
7588         GdkEventButton event;
7589         
7590         event.button = 3;
7591         event.time = gtk_get_current_event_time();
7592         event.x = 0;
7593         event.y = 0;
7594
7595         return text_clicked(compose->text, &event, compose);
7596 }
7597
7598 static gboolean compose_force_window_origin = TRUE;
7599 static Compose *compose_create(PrefsAccount *account,
7600                                                  FolderItem *folder,
7601                                                  ComposeMode mode,
7602                                                  gboolean batch)
7603 {
7604         Compose   *compose;
7605         GtkWidget *window;
7606         GtkWidget *vbox;
7607         GtkWidget *menubar;
7608         GtkWidget *handlebox;
7609
7610         GtkWidget *notebook;
7611         
7612         GtkWidget *attach_hbox;
7613         GtkWidget *attach_lab1;
7614         GtkWidget *attach_lab2;
7615
7616         GtkWidget *vbox2;
7617
7618         GtkWidget *label;
7619         GtkWidget *subject_hbox;
7620         GtkWidget *subject_frame;
7621         GtkWidget *subject_entry;
7622         GtkWidget *subject;
7623         GtkWidget *paned;
7624
7625         GtkWidget *edit_vbox;
7626         GtkWidget *ruler_hbox;
7627         GtkWidget *ruler;
7628         GtkWidget *scrolledwin;
7629         GtkWidget *text;
7630         GtkTextBuffer *buffer;
7631         GtkClipboard *clipboard;
7632
7633         UndoMain *undostruct;
7634
7635         GtkWidget *popupmenu;
7636         GtkWidget *tmpl_menu;
7637         GtkActionGroup *action_group = NULL;
7638
7639 #if USE_ENCHANT
7640         GtkAspell * gtkaspell = NULL;
7641 #endif
7642
7643         static GdkGeometry geometry;
7644
7645         cm_return_val_if_fail(account != NULL, NULL);
7646
7647         gtkut_convert_int_to_gdk_color(prefs_common.default_header_bgcolor,
7648                                            &default_header_bgcolor);
7649         gtkut_convert_int_to_gdk_color(prefs_common.default_header_color,
7650                                            &default_header_color);
7651
7652         debug_print("Creating compose window...\n");
7653         compose = g_new0(Compose, 1);
7654
7655         compose->batch = batch;
7656         compose->account = account;
7657         compose->folder = folder;
7658         
7659         compose->mutex = cm_mutex_new();
7660         compose->set_cursor_pos = -1;
7661
7662         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7663
7664         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7665         gtk_widget_set_size_request(window, prefs_common.compose_width,
7666                                         prefs_common.compose_height);
7667
7668         if (!geometry.max_width) {
7669                 geometry.max_width = gdk_screen_width();
7670                 geometry.max_height = gdk_screen_height();
7671         }
7672
7673         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7674                                       &geometry, GDK_HINT_MAX_SIZE);
7675         if (!geometry.min_width) {
7676                 geometry.min_width = 600;
7677                 geometry.min_height = 440;
7678         }
7679         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7680                                       &geometry, GDK_HINT_MIN_SIZE);
7681
7682 #ifndef GENERIC_UMPC    
7683         if (compose_force_window_origin)
7684                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
7685                                  prefs_common.compose_y);
7686 #endif
7687         g_signal_connect(G_OBJECT(window), "delete_event",
7688                          G_CALLBACK(compose_delete_cb), compose);
7689         MANAGE_WINDOW_SIGNALS_CONNECT(window);
7690         gtk_widget_realize(window);
7691
7692         gtkut_widget_set_composer_icon(window);
7693
7694         vbox = gtk_vbox_new(FALSE, 0);
7695         gtk_container_add(GTK_CONTAINER(window), vbox);
7696
7697         compose->ui_manager = gtk_ui_manager_new();
7698         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7699                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
7700         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7701                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7702         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7703                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7704         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7705                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7706         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7707                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7708
7709         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7710
7711         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7712         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7713 #ifdef USE_ENCHANT
7714         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7715 #endif
7716         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7717         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7718         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7719
7720 /* Compose menu */
7721         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7722         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7723         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7724         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7725         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7726         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7727         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM)
7728         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7729         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7730         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7731         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7732         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7733         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7734
7735 /* Edit menu */
7736         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7737         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7738         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7739
7740         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7741         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7742         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7743
7744         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7745         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7746         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7747         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7748
7749         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7750
7751         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7752         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7753         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7754         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7755         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7756         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7757         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7758         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7759         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7760         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7761         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7762         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7763         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7764         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7765         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7766
7767         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7768
7769         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7770         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7771         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7772         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7773         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7774
7775         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7776
7777         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7778
7779 #if USE_ENCHANT
7780 /* Spelling menu */
7781         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7782         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7783         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7784         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7785         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7786         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7787 #endif
7788
7789 /* Options menu */
7790         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7791         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7792         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7793         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7794         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7795
7796         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7797         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7798         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7799         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7800         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7801
7802         
7803         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7804         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7805         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7806         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7807         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7808         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7809         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7810
7811         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7812         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7813         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7814         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7815         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7816
7817         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7818
7819         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7820         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7821         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7822         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7823         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7824
7825         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7826         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_ISO_8859_1, "Options/Encoding/Western/"CS_ISO_8859_1, GTK_UI_MANAGER_MENUITEM)
7827         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_ISO_8859_15, "Options/Encoding/Western/"CS_ISO_8859_15, GTK_UI_MANAGER_MENUITEM)
7828         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7829
7830         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7831
7832         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7833         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Baltic", CS_ISO_8859_13, "Options/Encoding/Baltic/"CS_ISO_8859_13, GTK_UI_MANAGER_MENUITEM)
7834         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Baltic", CS_ISO_8859_4, "Options/Encoding/Baltic/"CS_ISO_8859_4, GTK_UI_MANAGER_MENUITEM)
7835
7836         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7837
7838         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7839         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_ISO_8859_8, "Options/Encoding/Hebrew/"CS_ISO_8859_8, GTK_UI_MANAGER_MENUITEM)
7840         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7841
7842         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7843         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_ISO_8859_6, "Options/Encoding/Arabic/"CS_ISO_8859_6, GTK_UI_MANAGER_MENUITEM)
7844         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7845
7846         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7847
7848         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7849         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_ISO_8859_5, "Options/Encoding/Cyrillic/"CS_ISO_8859_5, GTK_UI_MANAGER_MENUITEM)
7850         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7851         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR, "Options/Encoding/Cyrillic/"CS_MACCYR, GTK_UI_MANAGER_MENUITEM)
7852         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7853         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7854
7855         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7856         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_ISO_2022_JP, "Options/Encoding/Japanese/"CS_ISO_2022_JP, GTK_UI_MANAGER_MENUITEM)
7857         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_ISO_2022_JP_2, "Options/Encoding/Japanese/"CS_ISO_2022_JP_2, GTK_UI_MANAGER_MENUITEM)
7858         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7859         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7860
7861         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7862         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7863         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7864         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7865         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7866         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7867
7868         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7869         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7870         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_ISO_2022_KR, "Options/Encoding/Korean/"CS_ISO_2022_KR, GTK_UI_MANAGER_MENUITEM)
7871
7872         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7873         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7874         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7875 /* phew. */
7876
7877 /* Tools menu */
7878         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7879         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7880         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7881         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7882         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7883         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7884
7885 /* Help menu */
7886         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7887
7888         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7889         gtk_widget_show_all(menubar);
7890
7891         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7892         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7893
7894         if (prefs_common.toolbar_detachable) {
7895                 handlebox = gtk_handle_box_new();
7896         } else {
7897                 handlebox = gtk_hbox_new(FALSE, 0);
7898         }
7899         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7900
7901         gtk_widget_realize(handlebox);
7902         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7903                                           (gpointer)compose);
7904
7905         vbox2 = gtk_vbox_new(FALSE, 2);
7906         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7907         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7908         
7909         /* Notebook */
7910         notebook = gtk_notebook_new();
7911         gtk_widget_show(notebook);
7912
7913         /* header labels and entries */
7914         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7915                         compose_create_header(compose),
7916                         gtk_label_new_with_mnemonic(_("Hea_der")));
7917         /* attachment list */
7918         attach_hbox = gtk_hbox_new(FALSE, 0);
7919         gtk_widget_show(attach_hbox);
7920         
7921         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7922         gtk_widget_show(attach_lab1);
7923         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7924         
7925         attach_lab2 = gtk_label_new("");
7926         gtk_widget_show(attach_lab2);
7927         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7928         
7929         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7930                         compose_create_attach(compose),
7931                         attach_hbox);
7932         /* Others Tab */
7933         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7934                         compose_create_others(compose),
7935                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7936
7937         /* Subject */
7938         subject_hbox = gtk_hbox_new(FALSE, 0);
7939         gtk_widget_show(subject_hbox);
7940
7941         subject_frame = gtk_frame_new(NULL);
7942         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7943         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7944         gtk_widget_show(subject_frame);
7945
7946         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7947         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7948         gtk_widget_show(subject);
7949
7950         label = gtk_label_new_with_mnemonic(_("S_ubject:"));
7951         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7952         gtk_widget_show(label);
7953
7954 #ifdef USE_ENCHANT
7955         subject_entry = claws_spell_entry_new();
7956 #else
7957         subject_entry = gtk_entry_new();
7958 #endif
7959         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7960         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7961                          G_CALLBACK(compose_grab_focus_cb), compose);
7962         gtk_label_set_mnemonic_widget(GTK_LABEL(label), subject_entry);
7963         gtk_widget_show(subject_entry);
7964         compose->subject_entry = subject_entry;
7965         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7966         
7967         edit_vbox = gtk_vbox_new(FALSE, 0);
7968
7969         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7970
7971         /* ruler */
7972         ruler_hbox = gtk_hbox_new(FALSE, 0);
7973         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7974
7975         ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7976         gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7977         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7978                            BORDER_WIDTH);
7979
7980         /* text widget */
7981         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7982         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7983                                        GTK_POLICY_AUTOMATIC,
7984                                        GTK_POLICY_AUTOMATIC);
7985         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7986                                             GTK_SHADOW_IN);
7987         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7988
7989         text = gtk_text_view_new();
7990         if (prefs_common.show_compose_margin) {
7991                 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7992                 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7993         }
7994         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7995         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7996         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7997         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7998         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7999         
8000         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
8001         g_signal_connect_after(G_OBJECT(text), "size_allocate",
8002                                G_CALLBACK(compose_edit_size_alloc),
8003                                ruler);
8004         g_signal_connect(G_OBJECT(buffer), "changed",
8005                          G_CALLBACK(compose_changed_cb), compose);
8006         g_signal_connect(G_OBJECT(text), "grab_focus",
8007                          G_CALLBACK(compose_grab_focus_cb), compose);
8008         g_signal_connect(G_OBJECT(buffer), "insert_text",
8009                          G_CALLBACK(text_inserted), compose);
8010         g_signal_connect(G_OBJECT(text), "button_press_event",
8011                          G_CALLBACK(text_clicked), compose);
8012         g_signal_connect(G_OBJECT(text), "popup-menu",
8013                          G_CALLBACK(compose_popup_menu), compose);
8014         g_signal_connect(G_OBJECT(subject_entry), "changed",
8015                         G_CALLBACK(compose_changed_cb), compose);
8016         g_signal_connect(G_OBJECT(subject_entry), "activate",
8017                         G_CALLBACK(compose_subject_entry_activated), compose);
8018
8019         /* drag and drop */
8020         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
8021                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
8022                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
8023         g_signal_connect(G_OBJECT(text), "drag_data_received",
8024                          G_CALLBACK(compose_insert_drag_received_cb),
8025                          compose);
8026         g_signal_connect(G_OBJECT(text), "drag-drop",
8027                          G_CALLBACK(compose_drag_drop),
8028                          compose);
8029         g_signal_connect(G_OBJECT(text), "key-press-event",
8030                          G_CALLBACK(completion_set_focus_to_subject),
8031                          compose);
8032         gtk_widget_show_all(vbox);
8033
8034         /* pane between attach clist and text */
8035         paned = gtk_vpaned_new();
8036         gtk_container_add(GTK_CONTAINER(vbox2), paned);
8037         gtk_paned_pack1(GTK_PANED(paned), notebook, FALSE, FALSE);
8038         gtk_paned_pack2(GTK_PANED(paned), edit_vbox, TRUE, FALSE);
8039         gtk_paned_set_position(GTK_PANED(paned), prefs_common.compose_notebook_height);
8040         g_signal_connect(G_OBJECT(notebook), "size_allocate",
8041                          G_CALLBACK(compose_notebook_size_alloc), paned);
8042
8043         gtk_widget_show_all(paned);
8044
8045
8046         if (prefs_common.textfont) {
8047                 PangoFontDescription *font_desc;
8048
8049                 font_desc = pango_font_description_from_string
8050                         (prefs_common.textfont);
8051                 if (font_desc) {
8052                         gtk_widget_modify_font(text, font_desc);
8053                         pango_font_description_free(font_desc);
8054                 }
8055         }
8056
8057         gtk_action_group_add_actions(action_group, compose_popup_entries,
8058                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
8059         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
8060         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
8061         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
8062         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
8063         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
8064         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
8065         
8066         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
8067
8068         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8069         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8070         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
8071
8072         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
8073
8074         undostruct = undo_init(text);
8075         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
8076                                    compose);
8077
8078         address_completion_start(window);
8079
8080         compose->window        = window;
8081         compose->vbox          = vbox;
8082         compose->menubar       = menubar;
8083         compose->handlebox     = handlebox;
8084
8085         compose->vbox2         = vbox2;
8086
8087         compose->paned = paned;
8088
8089         compose->attach_label  = attach_lab2;
8090
8091         compose->notebook      = notebook;
8092         compose->edit_vbox     = edit_vbox;
8093         compose->ruler_hbox    = ruler_hbox;
8094         compose->ruler         = ruler;
8095         compose->scrolledwin   = scrolledwin;
8096         compose->text          = text;
8097
8098         compose->focused_editable = NULL;
8099
8100         compose->popupmenu    = popupmenu;
8101
8102         compose->tmpl_menu = tmpl_menu;
8103
8104         compose->mode = mode;
8105         compose->rmode = mode;
8106
8107         compose->targetinfo = NULL;
8108         compose->replyinfo  = NULL;
8109         compose->fwdinfo    = NULL;
8110
8111         compose->email_hashtable = g_hash_table_new_full(g_str_hash,
8112                                 g_str_equal, (GDestroyNotify) g_free, NULL);
8113         
8114         compose->replyto     = NULL;
8115         compose->cc          = NULL;
8116         compose->bcc         = NULL;
8117         compose->followup_to = NULL;
8118
8119         compose->ml_post     = NULL;
8120
8121         compose->inreplyto   = NULL;
8122         compose->references  = NULL;
8123         compose->msgid       = NULL;
8124         compose->boundary    = NULL;
8125
8126         compose->autowrap       = prefs_common.autowrap;
8127         compose->autoindent     = prefs_common.auto_indent;
8128         compose->use_signing    = FALSE;
8129         compose->use_encryption = FALSE;
8130         compose->privacy_system = NULL;
8131         compose->encdata        = NULL;
8132
8133         compose->modified = FALSE;
8134
8135         compose->return_receipt = FALSE;
8136
8137         compose->to_list        = NULL;
8138         compose->newsgroup_list = NULL;
8139
8140         compose->undostruct = undostruct;
8141
8142         compose->sig_str = NULL;
8143
8144         compose->exteditor_file    = NULL;
8145         compose->exteditor_pid     = -1;
8146         compose->exteditor_tag     = -1;
8147         compose->exteditor_socket  = NULL;
8148         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; /* inhibit auto-drafting while loading */
8149
8150         compose->folder_update_callback_id =
8151                 hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
8152                                 compose_update_folder_hook,
8153                                 (gpointer) compose);
8154
8155 #if USE_ENCHANT
8156         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
8157         if (mode != COMPOSE_REDIRECT) {
8158                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
8159                     strcmp(prefs_common.dictionary, "")) {
8160                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
8161                                                   prefs_common.alt_dictionary,
8162                                                   conv_get_locale_charset_str(),
8163                                                   prefs_common.misspelled_col,
8164                                                   prefs_common.check_while_typing,
8165                                                   prefs_common.recheck_when_changing_dict,
8166                                                   prefs_common.use_alternate,
8167                                                   prefs_common.use_both_dicts,
8168                                                   GTK_TEXT_VIEW(text),
8169                                                   GTK_WINDOW(compose->window),
8170                                                   compose_dict_changed,
8171                                                   compose_spell_menu_changed,
8172                                                   compose);
8173                         if (!gtkaspell) {
8174                                 alertpanel_error(_("Spell checker could not "
8175                                                 "be started.\n%s"),
8176                                                 gtkaspell_checkers_strerror());
8177                                 gtkaspell_checkers_reset_error();
8178                         } else {
8179                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
8180                         }
8181                 }
8182         }
8183         compose->gtkaspell = gtkaspell;
8184         compose_spell_menu_changed(compose);
8185         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
8186 #endif
8187
8188         compose_select_account(compose, account, TRUE);
8189
8190         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
8191         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
8192
8193         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
8194                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8195
8196         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
8197                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8198         
8199         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
8200                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8201
8202         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
8203         if (account->protocol != A_NNTP)
8204                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8205                                 prefs_common_translated_header_name("To:"));
8206         else
8207                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8208                                 prefs_common_translated_header_name("Newsgroups:"));
8209
8210 #ifndef USE_ALT_ADDRBOOK
8211         addressbook_set_target_compose(compose);
8212 #endif  
8213         if (mode != COMPOSE_REDIRECT)
8214                 compose_set_template_menu(compose);
8215         else {
8216                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
8217         }
8218
8219         compose_list = g_list_append(compose_list, compose);
8220
8221         if (!prefs_common.show_ruler)
8222                 gtk_widget_hide(ruler_hbox);
8223                 
8224         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
8225
8226         /* Priority */
8227         compose->priority = PRIORITY_NORMAL;
8228         compose_update_priority_menu_item(compose);
8229
8230         compose_set_out_encoding(compose);
8231         
8232         /* Actions menu */
8233         compose_update_actions_menu(compose);
8234
8235         /* Privacy Systems menu */
8236         compose_update_privacy_systems_menu(compose);
8237
8238         activate_privacy_system(compose, account, TRUE);
8239         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
8240         if (batch) {
8241                 gtk_widget_realize(window);
8242         } else {
8243                 gtk_widget_show(window);
8244         }
8245         
8246         return compose;
8247 }
8248
8249 static GtkWidget *compose_account_option_menu_create(Compose *compose)
8250 {
8251         GList *accounts;
8252         GtkWidget *hbox;
8253         GtkWidget *optmenu;
8254         GtkWidget *optmenubox;
8255         GtkWidget *fromlabel;
8256         GtkListStore *menu;
8257         GtkTreeIter iter;
8258         GtkWidget *from_name = NULL;
8259
8260         gint num = 0, def_menu = 0;
8261         
8262         accounts = account_get_list();
8263         cm_return_val_if_fail(accounts != NULL, NULL);
8264
8265         optmenubox = gtk_event_box_new();
8266         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
8267         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8268
8269         hbox = gtk_hbox_new(FALSE, 4);
8270         from_name = gtk_entry_new();
8271         
8272         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
8273                          G_CALLBACK(compose_grab_focus_cb), compose);
8274         g_signal_connect_after(G_OBJECT(from_name), "activate",
8275                          G_CALLBACK(from_name_activate_cb), optmenu);
8276
8277         for (; accounts != NULL; accounts = accounts->next, num++) {
8278                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
8279                 gchar *name, *from = NULL;
8280
8281                 if (ac == compose->account) def_menu = num;
8282
8283                 name = g_markup_printf_escaped("<i>%s</i>",
8284                                        ac->account_name);
8285                 
8286                 if (ac == compose->account) {
8287                         if (ac->name && *ac->name) {
8288                                 gchar *buf;
8289                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
8290                                 from = g_strdup_printf("%s <%s>",
8291                                                        buf, ac->address);
8292                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8293                         } else {
8294                                 from = g_strdup_printf("%s",
8295                                                        ac->address);
8296                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8297                         }
8298                         if (cur_account != compose->account) {
8299                                 gtk_widget_modify_base(
8300                                         GTK_WIDGET(from_name),
8301                                         GTK_STATE_NORMAL, &default_header_bgcolor);
8302                                 gtk_widget_modify_text(
8303                                         GTK_WIDGET(from_name),
8304                                         GTK_STATE_NORMAL, &default_header_color);
8305                         }
8306                 }
8307                 COMBOBOX_ADD(menu, name, ac->account_id);
8308                 g_free(name);
8309                 g_free(from);
8310         }
8311
8312         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
8313
8314         g_signal_connect(G_OBJECT(optmenu), "changed",
8315                         G_CALLBACK(account_activated),
8316                         compose);
8317         g_signal_connect(G_OBJECT(from_name), "populate-popup",
8318                          G_CALLBACK(compose_entry_popup_extend),
8319                          NULL);
8320
8321         fromlabel = gtk_label_new_with_mnemonic(_("_From:"));
8322         gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel), from_name);
8323
8324         gtk_box_pack_start(GTK_BOX(hbox), fromlabel, FALSE, FALSE, 4);
8325         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
8326         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
8327
8328         /* Putting only the GtkEntry into focus chain of parent hbox causes
8329          * the account selector combobox next to it to be unreachable when
8330          * navigating widgets in GtkTable with up/down arrow keys.
8331          * Note: gtk_widget_set_can_focus() was not enough. */
8332         GList *l = NULL;
8333         l = g_list_prepend(l, from_name);
8334         gtk_container_set_focus_chain(GTK_CONTAINER(hbox), l);
8335         g_list_free(l);
8336         
8337         CLAWS_SET_TIP(optmenubox,
8338                 _("Account to use for this email"));
8339         CLAWS_SET_TIP(from_name,
8340                 _("Sender address to be used"));
8341
8342         compose->account_combo = optmenu;
8343         compose->from_name = from_name;
8344         
8345         return hbox;
8346 }
8347
8348 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8349 {
8350         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8351         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8352         Compose *compose = (Compose *) data;
8353         if (active) {
8354                 compose->priority = value;
8355         }
8356 }
8357
8358 static void compose_reply_change_mode(Compose *compose,
8359                                     ComposeMode action)
8360 {
8361         gboolean was_modified = compose->modified;
8362
8363         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
8364         
8365         cm_return_if_fail(compose->replyinfo != NULL);
8366         
8367         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
8368                 ml = TRUE;
8369         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
8370                 followup = TRUE;
8371         if (action == COMPOSE_REPLY_TO_ALL)
8372                 all = TRUE;
8373         if (action == COMPOSE_REPLY_TO_SENDER)
8374                 sender = TRUE;
8375         if (action == COMPOSE_REPLY_TO_LIST)
8376                 ml = TRUE;
8377
8378         compose_remove_header_entries(compose);
8379         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
8380         if (compose->account->set_autocc && compose->account->auto_cc)
8381                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8382
8383         if (compose->account->set_autobcc && compose->account->auto_bcc) 
8384                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8385         
8386         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8387                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8388         compose_show_first_last_header(compose, TRUE);
8389         compose->modified = was_modified;
8390         compose_set_title(compose);
8391 }
8392
8393 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8394 {
8395         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8396         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8397         Compose *compose = (Compose *) data;
8398         
8399         if (active)
8400                 compose_reply_change_mode(compose, value);
8401 }
8402
8403 static void compose_update_priority_menu_item(Compose * compose)
8404 {
8405         GtkWidget *menuitem = NULL;
8406         switch (compose->priority) {
8407                 case PRIORITY_HIGHEST:
8408                         menuitem = gtk_ui_manager_get_widget
8409                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8410                         break;
8411                 case PRIORITY_HIGH:
8412                         menuitem = gtk_ui_manager_get_widget
8413                                 (compose->ui_manager, "/Menu/Options/Priority/High");
8414                         break;
8415                 case PRIORITY_NORMAL:
8416                         menuitem = gtk_ui_manager_get_widget
8417                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8418                         break;
8419                 case PRIORITY_LOW:
8420                         menuitem = gtk_ui_manager_get_widget
8421                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
8422                         break;
8423                 case PRIORITY_LOWEST:
8424                         menuitem = gtk_ui_manager_get_widget
8425                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8426                         break;
8427         }
8428         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8429 }       
8430
8431 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8432 {
8433         Compose *compose = (Compose *) data;
8434         gchar *systemid;
8435         gboolean can_sign = FALSE, can_encrypt = FALSE;
8436
8437         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8438
8439         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8440                 return;
8441
8442         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8443         g_free(compose->privacy_system);
8444         compose->privacy_system = NULL;
8445         g_free(compose->encdata);
8446         compose->encdata = NULL;
8447         if (systemid != NULL) {
8448                 compose->privacy_system = g_strdup(systemid);
8449
8450                 can_sign = privacy_system_can_sign(systemid);
8451                 can_encrypt = privacy_system_can_encrypt(systemid);
8452         }
8453
8454         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8455
8456         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8457         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8458 }
8459
8460 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8461 {
8462         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8463         GtkWidget *menuitem = NULL;
8464         GList *children, *amenu;
8465         gboolean can_sign = FALSE, can_encrypt = FALSE;
8466         gboolean found = FALSE;
8467
8468         if (compose->privacy_system != NULL) {
8469                 gchar *systemid;
8470                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8471                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8472                 cm_return_if_fail(menuitem != NULL);
8473
8474                 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8475                 amenu = children;
8476                 menuitem = NULL;
8477                 while (amenu != NULL) {
8478                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8479                         if (systemid != NULL) {
8480                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
8481                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8482                                         menuitem = GTK_WIDGET(amenu->data);
8483
8484                                         can_sign = privacy_system_can_sign(systemid);
8485                                         can_encrypt = privacy_system_can_encrypt(systemid);
8486                                         found = TRUE;
8487                                         break;
8488                                 } 
8489                         } else if (strlen(compose->privacy_system) == 0 && 
8490                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8491                                         menuitem = GTK_WIDGET(amenu->data);
8492
8493                                         can_sign = FALSE;
8494                                         can_encrypt = FALSE;
8495                                         found = TRUE;
8496                                         break;
8497                         }
8498
8499                         amenu = amenu->next;
8500                 }
8501                 g_list_free(children);
8502                 if (menuitem != NULL)
8503                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8504                 
8505                 if (warn && !found && strlen(compose->privacy_system)) {
8506                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8507                                   "will not be able to sign or encrypt this message."),
8508                                   compose->privacy_system);
8509                 }
8510         } 
8511
8512         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8513         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8514 }       
8515  
8516 static void compose_set_out_encoding(Compose *compose)
8517 {
8518         CharSet out_encoding;
8519         const gchar *branch = NULL;
8520         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8521
8522         switch(out_encoding) {
8523                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8524                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8525                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8526                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8527                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8528                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8529                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8530                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8531                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8532                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8533                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8534                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8535                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8536                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8537                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8538                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8539                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8540                 case C_MACCYR: branch = "Menu/Options/Encoding/Cyrillic/" CS_MACCYR; break;
8541                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8542                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8543                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8544                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8545                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8546                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8547                 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8548                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8549                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8550                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8551                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8552                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8553                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8554                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8555                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8556                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8557         }
8558         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8559 }
8560
8561 static void compose_set_template_menu(Compose *compose)
8562 {
8563         GSList *tmpl_list, *cur;
8564         GtkWidget *menu;
8565         GtkWidget *item;
8566
8567         tmpl_list = template_get_config();
8568
8569         menu = gtk_menu_new();
8570
8571         gtk_menu_set_accel_group (GTK_MENU (menu), 
8572                 gtk_ui_manager_get_accel_group(compose->ui_manager));
8573         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8574                 Template *tmpl = (Template *)cur->data;
8575                 gchar *accel_path = NULL;
8576                 item = gtk_menu_item_new_with_label(tmpl->name);
8577                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8578                 g_signal_connect(G_OBJECT(item), "activate",
8579                                  G_CALLBACK(compose_template_activate_cb),
8580                                  compose);
8581                 g_object_set_data(G_OBJECT(item), "template", tmpl);
8582                 gtk_widget_show(item);
8583                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8584                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8585                 g_free(accel_path);
8586         }
8587
8588         gtk_widget_show(menu);
8589         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8590 }
8591
8592 void compose_update_actions_menu(Compose *compose)
8593 {
8594         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8595 }
8596
8597 static void compose_update_privacy_systems_menu(Compose *compose)
8598 {
8599         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8600         GSList *systems, *cur;
8601         GtkWidget *widget;
8602         GtkWidget *system_none;
8603         GSList *group;
8604         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8605         GtkWidget *privacy_menu = gtk_menu_new();
8606
8607         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8608         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8609
8610         g_signal_connect(G_OBJECT(system_none), "activate",
8611                 G_CALLBACK(compose_set_privacy_system_cb), compose);
8612
8613         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8614         gtk_widget_show(system_none);
8615
8616         systems = privacy_get_system_ids();
8617         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8618                 gchar *systemid = cur->data;
8619
8620                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8621                 widget = gtk_radio_menu_item_new_with_label(group,
8622                         privacy_system_get_name(systemid));
8623                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8624                                        g_strdup(systemid), g_free);
8625                 g_signal_connect(G_OBJECT(widget), "activate",
8626                         G_CALLBACK(compose_set_privacy_system_cb), compose);
8627
8628                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8629                 gtk_widget_show(widget);
8630                 g_free(systemid);
8631         }
8632         g_slist_free(systems);
8633         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8634         gtk_widget_show_all(privacy_menu);
8635         gtk_widget_show_all(privacy_menuitem);
8636 }
8637
8638 void compose_reflect_prefs_all(void)
8639 {
8640         GList *cur;
8641         Compose *compose;
8642
8643         for (cur = compose_list; cur != NULL; cur = cur->next) {
8644                 compose = (Compose *)cur->data;
8645                 compose_set_template_menu(compose);
8646         }
8647 }
8648
8649 void compose_reflect_prefs_pixmap_theme(void)
8650 {
8651         GList *cur;
8652         Compose *compose;
8653
8654         for (cur = compose_list; cur != NULL; cur = cur->next) {
8655                 compose = (Compose *)cur->data;
8656                 toolbar_update(TOOLBAR_COMPOSE, compose);
8657         }
8658 }
8659
8660 static const gchar *compose_quote_char_from_context(Compose *compose)
8661 {
8662         const gchar *qmark = NULL;
8663
8664         cm_return_val_if_fail(compose != NULL, NULL);
8665
8666         switch (compose->mode) {
8667                 /* use forward-specific quote char */
8668                 case COMPOSE_FORWARD:
8669                 case COMPOSE_FORWARD_AS_ATTACH:
8670                 case COMPOSE_FORWARD_INLINE:
8671                         if (compose->folder && compose->folder->prefs &&
8672                                         compose->folder->prefs->forward_with_format)
8673                                 qmark = compose->folder->prefs->forward_quotemark;
8674                         else if (compose->account->forward_with_format)
8675                                 qmark = compose->account->forward_quotemark;
8676                         else
8677                                 qmark = prefs_common.fw_quotemark;
8678                         break;
8679
8680                 /* use reply-specific quote char in all other modes */
8681                 default:
8682                         if (compose->folder && compose->folder->prefs &&
8683                                         compose->folder->prefs->reply_with_format)
8684                                 qmark = compose->folder->prefs->reply_quotemark;
8685                         else if (compose->account->reply_with_format)
8686                                 qmark = compose->account->reply_quotemark;
8687                         else
8688                                 qmark = prefs_common.quotemark;
8689                         break;
8690         }
8691
8692         if (qmark == NULL || *qmark == '\0')
8693                 qmark = "> ";
8694
8695         return qmark;
8696 }
8697
8698 static void compose_template_apply(Compose *compose, Template *tmpl,
8699                                    gboolean replace)
8700 {
8701         GtkTextView *text;
8702         GtkTextBuffer *buffer;
8703         GtkTextMark *mark;
8704         GtkTextIter iter;
8705         const gchar *qmark;
8706         gchar *parsed_str = NULL;
8707         gint cursor_pos = 0;
8708         const gchar *err_msg = _("The body of the template has an error at line %d.");
8709         if (!tmpl) return;
8710
8711         /* process the body */
8712
8713         text = GTK_TEXT_VIEW(compose->text);
8714         buffer = gtk_text_view_get_buffer(text);
8715
8716         if (tmpl->value) {
8717                 qmark = compose_quote_char_from_context(compose);
8718
8719                 if (compose->replyinfo != NULL) {
8720
8721                         if (replace)
8722                                 gtk_text_buffer_set_text(buffer, "", -1);
8723                         mark = gtk_text_buffer_get_insert(buffer);
8724                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8725
8726                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8727                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8728
8729                 } else if (compose->fwdinfo != NULL) {
8730
8731                         if (replace)
8732                                 gtk_text_buffer_set_text(buffer, "", -1);
8733                         mark = gtk_text_buffer_get_insert(buffer);
8734                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8735
8736                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8737                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8738
8739                 } else {
8740                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8741
8742                         GtkTextIter start, end;
8743                         gchar *tmp = NULL;
8744
8745                         gtk_text_buffer_get_start_iter(buffer, &start);
8746                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8747                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8748
8749                         /* clear the buffer now */
8750                         if (replace)
8751                                 gtk_text_buffer_set_text(buffer, "", -1);
8752
8753                         parsed_str = compose_quote_fmt(compose, dummyinfo,
8754                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8755                         procmsg_msginfo_free( &dummyinfo );
8756
8757                         g_free( tmp );
8758                 } 
8759         } else {
8760                 if (replace)
8761                         gtk_text_buffer_set_text(buffer, "", -1);
8762                 mark = gtk_text_buffer_get_insert(buffer);
8763                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8764         }       
8765
8766         if (replace && parsed_str && compose->account->auto_sig)
8767                 compose_insert_sig(compose, FALSE);
8768
8769         if (replace && parsed_str) {
8770                 gtk_text_buffer_get_start_iter(buffer, &iter);
8771                 gtk_text_buffer_place_cursor(buffer, &iter);
8772         }
8773         
8774         if (parsed_str) {
8775                 cursor_pos = quote_fmt_get_cursor_pos();
8776                 compose->set_cursor_pos = cursor_pos;
8777                 if (cursor_pos == -1)
8778                         cursor_pos = 0;
8779                 gtk_text_buffer_get_start_iter(buffer, &iter);
8780                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8781                 gtk_text_buffer_place_cursor(buffer, &iter);
8782         }
8783
8784         /* process the other fields */
8785
8786         compose_template_apply_fields(compose, tmpl);
8787         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8788         quote_fmt_reset_vartable();
8789         compose_changed_cb(NULL, compose);
8790
8791 #ifdef USE_ENCHANT
8792         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8793                 gtkaspell_highlight_all(compose->gtkaspell);
8794 #endif
8795 }
8796
8797 static void compose_template_apply_fields_error(const gchar *header)
8798 {
8799         gchar *tr;
8800         gchar *text;
8801
8802         tr = g_strdup(C_("'%s' stands for a header name",
8803                                   "Template '%s' format error."));
8804         text = g_strdup_printf(tr, prefs_common_translated_header_name(header));
8805         alertpanel_error("%s", text);
8806
8807         g_free(text);
8808         g_free(tr);
8809 }
8810
8811 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8812 {
8813         MsgInfo* dummyinfo = NULL;
8814         MsgInfo *msginfo = NULL;
8815         gchar *buf = NULL;
8816
8817         if (compose->replyinfo != NULL)
8818                 msginfo = compose->replyinfo;
8819         else if (compose->fwdinfo != NULL)
8820                 msginfo = compose->fwdinfo;
8821         else {
8822                 dummyinfo = compose_msginfo_new_from_compose(compose);
8823                 msginfo = dummyinfo;
8824         }
8825
8826         if (tmpl->from && *tmpl->from != '\0') {
8827 #ifdef USE_ENCHANT
8828                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8829                                 compose->gtkaspell);
8830 #else
8831                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8832 #endif
8833                 quote_fmt_scan_string(tmpl->from);
8834                 quote_fmt_parse();
8835
8836                 buf = quote_fmt_get_buffer();
8837                 if (buf == NULL) {
8838                         compose_template_apply_fields_error("From");
8839                 } else {
8840                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8841                 }
8842         }
8843
8844         if (tmpl->to && *tmpl->to != '\0') {
8845 #ifdef USE_ENCHANT
8846                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8847                                 compose->gtkaspell);
8848 #else
8849                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8850 #endif
8851                 quote_fmt_scan_string(tmpl->to);
8852                 quote_fmt_parse();
8853
8854                 buf = quote_fmt_get_buffer();
8855                 if (buf == NULL) {
8856                         compose_template_apply_fields_error("To");
8857                 } else {
8858                         compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8859                 }
8860         }
8861
8862         if (tmpl->cc && *tmpl->cc != '\0') {
8863 #ifdef USE_ENCHANT
8864                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8865                                 compose->gtkaspell);
8866 #else
8867                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8868 #endif
8869                 quote_fmt_scan_string(tmpl->cc);
8870                 quote_fmt_parse();
8871
8872                 buf = quote_fmt_get_buffer();
8873                 if (buf == NULL) {
8874                         compose_template_apply_fields_error("Cc");
8875                 } else {
8876                         compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8877                 }
8878         }
8879
8880         if (tmpl->bcc && *tmpl->bcc != '\0') {
8881 #ifdef USE_ENCHANT
8882                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8883                                 compose->gtkaspell);
8884 #else
8885                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8886 #endif
8887                 quote_fmt_scan_string(tmpl->bcc);
8888                 quote_fmt_parse();
8889
8890                 buf = quote_fmt_get_buffer();
8891                 if (buf == NULL) {
8892                         compose_template_apply_fields_error("Bcc");
8893                 } else {
8894                         compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8895                 }
8896         }
8897
8898         if (tmpl->replyto && *tmpl->replyto != '\0') {
8899 #ifdef USE_ENCHANT
8900                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8901                                 compose->gtkaspell);
8902 #else
8903                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8904 #endif
8905                 quote_fmt_scan_string(tmpl->replyto);
8906                 quote_fmt_parse();
8907
8908                 buf = quote_fmt_get_buffer();
8909                 if (buf == NULL) {
8910                         compose_template_apply_fields_error("Reply-To");
8911                 } else {
8912                         compose_entry_append(compose, buf, COMPOSE_REPLYTO, PREF_TEMPLATE);
8913                 }
8914         }
8915
8916         /* process the subject */
8917         if (tmpl->subject && *tmpl->subject != '\0') {
8918 #ifdef USE_ENCHANT
8919                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8920                                 compose->gtkaspell);
8921 #else
8922                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8923 #endif
8924                 quote_fmt_scan_string(tmpl->subject);
8925                 quote_fmt_parse();
8926
8927                 buf = quote_fmt_get_buffer();
8928                 if (buf == NULL) {
8929                         compose_template_apply_fields_error("Subject");
8930                 } else {
8931                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8932                 }
8933         }
8934
8935         procmsg_msginfo_free( &dummyinfo );
8936 }
8937
8938 static void compose_destroy(Compose *compose)
8939 {
8940         GtkAllocation allocation;
8941         GtkTextBuffer *buffer;
8942         GtkClipboard *clipboard;
8943
8944         compose_list = g_list_remove(compose_list, compose);
8945
8946         if (compose->updating) {
8947                 debug_print("danger, not destroying anything now\n");
8948                 compose->deferred_destroy = TRUE;
8949                 return;
8950         }
8951
8952         /* NOTE: address_completion_end() does nothing with the window
8953          * however this may change. */
8954         address_completion_end(compose->window);
8955
8956         slist_free_strings_full(compose->to_list);
8957         slist_free_strings_full(compose->newsgroup_list);
8958         slist_free_strings_full(compose->header_list);
8959
8960         slist_free_strings_full(extra_headers);
8961         extra_headers = NULL;
8962
8963         compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8964
8965         g_hash_table_destroy(compose->email_hashtable);
8966
8967         hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST,
8968                         compose->folder_update_callback_id);
8969
8970         procmsg_msginfo_free(&(compose->targetinfo));
8971         procmsg_msginfo_free(&(compose->replyinfo));
8972         procmsg_msginfo_free(&(compose->fwdinfo));
8973
8974         g_free(compose->replyto);
8975         g_free(compose->cc);
8976         g_free(compose->bcc);
8977         g_free(compose->newsgroups);
8978         g_free(compose->followup_to);
8979
8980         g_free(compose->ml_post);
8981
8982         g_free(compose->inreplyto);
8983         g_free(compose->references);
8984         g_free(compose->msgid);
8985         g_free(compose->boundary);
8986
8987         g_free(compose->redirect_filename);
8988         if (compose->undostruct)
8989                 undo_destroy(compose->undostruct);
8990
8991         g_free(compose->sig_str);
8992
8993         g_free(compose->exteditor_file);
8994
8995         g_free(compose->orig_charset);
8996
8997         g_free(compose->privacy_system);
8998         g_free(compose->encdata);
8999
9000 #ifndef USE_ALT_ADDRBOOK
9001         if (addressbook_get_target_compose() == compose)
9002                 addressbook_set_target_compose(NULL);
9003 #endif
9004 #if USE_ENCHANT
9005         if (compose->gtkaspell) {
9006                 gtkaspell_delete(compose->gtkaspell);
9007                 compose->gtkaspell = NULL;
9008         }
9009 #endif
9010
9011         if (!compose->batch) {
9012                 gtk_widget_get_allocation(compose->window, &allocation);
9013                 prefs_common.compose_width = allocation.width;
9014                 prefs_common.compose_height = allocation.height;
9015         }
9016
9017         if (!gtk_widget_get_parent(compose->paned))
9018                 gtk_widget_destroy(compose->paned);
9019         gtk_widget_destroy(compose->popupmenu);
9020
9021         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
9022         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
9023         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
9024
9025         gtk_widget_destroy(compose->window);
9026         toolbar_destroy(compose->toolbar);
9027         g_free(compose->toolbar);
9028         cm_mutex_free(compose->mutex);
9029         g_free(compose);
9030 }
9031
9032 static void compose_attach_info_free(AttachInfo *ainfo)
9033 {
9034         g_free(ainfo->file);
9035         g_free(ainfo->content_type);
9036         g_free(ainfo->name);
9037         g_free(ainfo->charset);
9038         g_free(ainfo);
9039 }
9040
9041 static void compose_attach_update_label(Compose *compose)
9042 {
9043         GtkTreeIter iter;
9044         gint i = 1;
9045         gchar *text;
9046         GtkTreeModel *model;
9047         goffset total_size;
9048         AttachInfo *ainfo;
9049
9050         if (compose == NULL)
9051                 return;
9052
9053         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
9054         if (!gtk_tree_model_get_iter_first(model, &iter)) {
9055                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
9056                 return;
9057         }
9058
9059         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
9060         total_size = ainfo->size;
9061         while(gtk_tree_model_iter_next(model, &iter)) {
9062                 gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1);
9063                 total_size += ainfo->size;
9064                 i++;
9065         }
9066         text = g_strdup_printf(" (%d/%s)", i, to_human_readable(total_size));
9067         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
9068         g_free(text);
9069 }
9070
9071 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
9072 {
9073         Compose *compose = (Compose *)data;
9074         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
9075         GtkTreeSelection *selection;
9076         GList *sel, *cur;
9077         GtkTreeModel *model;
9078
9079         selection = gtk_tree_view_get_selection(tree_view);
9080         sel = gtk_tree_selection_get_selected_rows(selection, &model);
9081
9082         if (!sel) 
9083                 return;
9084
9085         for (cur = sel; cur != NULL; cur = cur->next) {
9086                 GtkTreePath *path = cur->data;
9087                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
9088                                                 (model, cur->data);
9089                 cur->data = ref;
9090                 gtk_tree_path_free(path);
9091         }
9092
9093         for (cur = sel; cur != NULL; cur = cur->next) {
9094                 GtkTreeRowReference *ref = cur->data;
9095                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
9096                 GtkTreeIter iter;
9097
9098                 if (gtk_tree_model_get_iter(model, &iter, path))
9099                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
9100                 
9101                 gtk_tree_path_free(path);
9102                 gtk_tree_row_reference_free(ref);
9103         }
9104
9105         g_list_free(sel);
9106         compose_attach_update_label(compose);
9107 }
9108
9109 static struct _AttachProperty
9110 {
9111         GtkWidget *window;
9112         GtkWidget *mimetype_entry;
9113         GtkWidget *encoding_optmenu;
9114         GtkWidget *path_entry;
9115         GtkWidget *filename_entry;
9116         GtkWidget *ok_btn;
9117         GtkWidget *cancel_btn;
9118 } attach_prop;
9119
9120 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
9121 {       
9122         gtk_tree_path_free((GtkTreePath *)ptr);
9123 }
9124
9125 static void compose_attach_property(GtkAction *action, gpointer data)
9126 {
9127         Compose *compose = (Compose *)data;
9128         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
9129         AttachInfo *ainfo;
9130         GtkComboBox *optmenu;
9131         GtkTreeSelection *selection;
9132         GList *sel;
9133         GtkTreeModel *model;
9134         GtkTreeIter iter;
9135         GtkTreePath *path;
9136         static gboolean cancelled;
9137
9138         /* only if one selected */
9139         selection = gtk_tree_view_get_selection(tree_view);
9140         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
9141                 return;
9142
9143         sel = gtk_tree_selection_get_selected_rows(selection, &model);
9144         if (!sel)
9145                 return;
9146
9147         path = (GtkTreePath *) sel->data;
9148         gtk_tree_model_get_iter(model, &iter, path);
9149         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
9150         
9151         if (!ainfo) {
9152                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
9153                 g_list_free(sel);
9154                 return;
9155         }               
9156         g_list_free(sel);
9157
9158         if (!attach_prop.window)
9159                 compose_attach_property_create(&cancelled);
9160         gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
9161         gtk_widget_grab_focus(attach_prop.ok_btn);
9162         gtk_widget_show(attach_prop.window);
9163         gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
9164                         GTK_WINDOW(compose->window));
9165
9166         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
9167         if (ainfo->encoding == ENC_UNKNOWN)
9168                 combobox_select_by_data(optmenu, ENC_BASE64);
9169         else
9170                 combobox_select_by_data(optmenu, ainfo->encoding);
9171
9172         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
9173                            ainfo->content_type ? ainfo->content_type : "");
9174         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
9175                            ainfo->file ? ainfo->file : "");
9176         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
9177                            ainfo->name ? ainfo->name : "");
9178
9179         for (;;) {
9180                 const gchar *entry_text;
9181                 gchar *text;
9182                 gchar *cnttype = NULL;
9183                 gchar *file = NULL;
9184                 off_t size = 0;
9185
9186                 cancelled = FALSE;
9187                 gtk_main();
9188
9189                 gtk_widget_hide(attach_prop.window);
9190                 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
9191                 
9192                 if (cancelled)
9193                         break;
9194
9195                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
9196                 if (*entry_text != '\0') {
9197                         gchar *p;
9198
9199                         text = g_strstrip(g_strdup(entry_text));
9200                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
9201                                 cnttype = g_strdup(text);
9202                                 g_free(text);
9203                         } else {
9204                                 alertpanel_error(_("Invalid MIME type."));
9205                                 g_free(text);
9206                                 continue;
9207                         }
9208                 }
9209
9210                 ainfo->encoding = combobox_get_active_data(optmenu);
9211
9212                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
9213                 if (*entry_text != '\0') {
9214                         if (is_file_exist(entry_text) &&
9215                             (size = get_file_size(entry_text)) > 0)
9216                                 file = g_strdup(entry_text);
9217                         else {
9218                                 alertpanel_error
9219                                         (_("File doesn't exist or is empty."));
9220                                 g_free(cnttype);
9221                                 continue;
9222                         }
9223                 }
9224
9225                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
9226                 if (*entry_text != '\0') {
9227                         g_free(ainfo->name);
9228                         ainfo->name = g_strdup(entry_text);
9229                 }
9230
9231                 if (cnttype) {
9232                         g_free(ainfo->content_type);
9233                         ainfo->content_type = cnttype;
9234                 }
9235                 if (file) {
9236                         g_free(ainfo->file);
9237                         ainfo->file = file;
9238                 }
9239                 if (size)
9240                         ainfo->size = (goffset)size;
9241
9242                 /* update tree store */
9243                 text = to_human_readable(ainfo->size);
9244                 gtk_tree_model_get_iter(model, &iter, path);
9245                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
9246                                    COL_MIMETYPE, ainfo->content_type,
9247                                    COL_SIZE, text,
9248                                    COL_NAME, ainfo->name,
9249                                    COL_CHARSET, ainfo->charset,
9250                                    -1);
9251                 
9252                 break;
9253         }
9254
9255         gtk_tree_path_free(path);
9256 }
9257
9258 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9259 { \
9260         label = gtk_label_new(str); \
9261         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
9262                          GTK_FILL, 0, 0, 0); \
9263         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
9264  \
9265         entry = gtk_entry_new(); \
9266         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
9267                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
9268 }
9269
9270 static void compose_attach_property_create(gboolean *cancelled)
9271 {
9272         GtkWidget *window;
9273         GtkWidget *vbox;
9274         GtkWidget *table;
9275         GtkWidget *label;
9276         GtkWidget *mimetype_entry;
9277         GtkWidget *hbox;
9278         GtkWidget *optmenu;
9279         GtkListStore *optmenu_menu;
9280         GtkWidget *path_entry;
9281         GtkWidget *filename_entry;
9282         GtkWidget *hbbox;
9283         GtkWidget *ok_btn;
9284         GtkWidget *cancel_btn;
9285         GList     *mime_type_list, *strlist;
9286         GtkTreeIter iter;
9287
9288         debug_print("Creating attach_property window...\n");
9289
9290         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
9291         gtk_widget_set_size_request(window, 480, -1);
9292         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
9293         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
9294         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
9295         g_signal_connect(G_OBJECT(window), "delete_event",
9296                          G_CALLBACK(attach_property_delete_event),
9297                          cancelled);
9298         g_signal_connect(G_OBJECT(window), "key_press_event",
9299                          G_CALLBACK(attach_property_key_pressed),
9300                          cancelled);
9301
9302         vbox = gtk_vbox_new(FALSE, 8);
9303         gtk_container_add(GTK_CONTAINER(window), vbox);
9304
9305         table = gtk_table_new(4, 2, FALSE);
9306         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
9307         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
9308         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
9309
9310         label = gtk_label_new(_("MIME type")); 
9311         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
9312                          GTK_FILL, 0, 0, 0); 
9313         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
9314 #if !GTK_CHECK_VERSION(2, 24, 0)
9315         mimetype_entry = gtk_combo_box_entry_new_text(); 
9316 #else
9317         mimetype_entry = gtk_combo_box_text_new_with_entry();
9318 #endif
9319         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
9320                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9321                          
9322         /* stuff with list */
9323         mime_type_list = procmime_get_mime_type_list();
9324         strlist = NULL;
9325         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
9326                 MimeType *type = (MimeType *) mime_type_list->data;
9327                 gchar *tmp;
9328
9329                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
9330
9331                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
9332                         g_free(tmp);
9333                 else
9334                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
9335                                         (GCompareFunc)strcmp2);
9336         }
9337
9338         for (mime_type_list = strlist; mime_type_list != NULL; 
9339                 mime_type_list = mime_type_list->next) {
9340 #if !GTK_CHECK_VERSION(2, 24, 0)
9341                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
9342 #else
9343                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
9344 #endif
9345                 g_free(mime_type_list->data);
9346         }
9347         g_list_free(strlist);
9348         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
9349         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
9350
9351         label = gtk_label_new(_("Encoding"));
9352         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
9353                          GTK_FILL, 0, 0, 0);
9354         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
9355
9356         hbox = gtk_hbox_new(FALSE, 0);
9357         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
9358                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9359
9360         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
9361         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
9362
9363         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
9364         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
9365         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
9366         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
9367         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
9368
9369         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
9370
9371         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
9372         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
9373
9374         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
9375                                       &ok_btn, GTK_STOCK_OK,
9376                                       NULL, NULL);
9377         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
9378         gtk_widget_grab_default(ok_btn);
9379
9380         g_signal_connect(G_OBJECT(ok_btn), "clicked",
9381                          G_CALLBACK(attach_property_ok),
9382                          cancelled);
9383         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
9384                          G_CALLBACK(attach_property_cancel),
9385                          cancelled);
9386
9387         gtk_widget_show_all(vbox);
9388
9389         attach_prop.window           = window;
9390         attach_prop.mimetype_entry   = mimetype_entry;
9391         attach_prop.encoding_optmenu = optmenu;
9392         attach_prop.path_entry       = path_entry;
9393         attach_prop.filename_entry   = filename_entry;
9394         attach_prop.ok_btn           = ok_btn;
9395         attach_prop.cancel_btn       = cancel_btn;
9396 }
9397
9398 #undef SET_LABEL_AND_ENTRY
9399
9400 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
9401 {
9402         *cancelled = FALSE;
9403         gtk_main_quit();
9404 }
9405
9406 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
9407 {
9408         *cancelled = TRUE;
9409         gtk_main_quit();
9410 }
9411
9412 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
9413                                          gboolean *cancelled)
9414 {
9415         *cancelled = TRUE;
9416         gtk_main_quit();
9417
9418         return TRUE;
9419 }
9420
9421 static gboolean attach_property_key_pressed(GtkWidget *widget,
9422                                             GdkEventKey *event,
9423                                             gboolean *cancelled)
9424 {
9425         if (event && event->keyval == GDK_KEY_Escape) {
9426                 *cancelled = TRUE;
9427                 gtk_main_quit();
9428         }
9429         if (event && event->keyval == GDK_KEY_Return) {
9430                 *cancelled = FALSE;
9431                 gtk_main_quit();
9432                 return TRUE;
9433         }
9434         return FALSE;
9435 }
9436
9437 static void compose_exec_ext_editor(Compose *compose)
9438 {
9439 #ifdef G_OS_UNIX
9440         gchar *tmp;
9441         GtkWidget *socket;
9442         GdkNativeWindow socket_wid = 0;
9443         pid_t pid;
9444         gint pipe_fds[2];
9445
9446         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9447                               G_DIR_SEPARATOR, compose);
9448
9449         if (compose_get_ext_editor_uses_socket()) {
9450                 /* Only allow one socket */
9451                 if (compose->exteditor_socket != NULL) {
9452                         if (gtk_widget_is_focus(compose->exteditor_socket)) {
9453                                 /* Move the focus off of the socket */
9454                                 gtk_widget_child_focus(compose->window, GTK_DIR_TAB_BACKWARD);
9455                         }
9456                         g_free(tmp);
9457                         return;
9458                 }
9459                 /* Create the receiving GtkSocket */
9460                 socket = gtk_socket_new ();
9461                 g_signal_connect (GTK_OBJECT(socket), "plug-removed",
9462                                   G_CALLBACK(compose_ext_editor_plug_removed_cb),
9463                                   compose);
9464                 gtk_box_pack_start(GTK_BOX(compose->edit_vbox), socket, TRUE, TRUE, 0);
9465                 gtk_widget_set_size_request(socket, prefs_common.compose_width, -1);
9466                 /* Realize the socket so that we can use its ID */
9467                 gtk_widget_realize(socket);
9468                 socket_wid = gtk_socket_get_id(GTK_SOCKET (socket));
9469                 compose->exteditor_socket = socket;
9470         }
9471
9472         if (pipe(pipe_fds) < 0) {
9473                 perror("pipe");
9474                 g_free(tmp);
9475                 return;
9476         }
9477
9478         if ((pid = fork()) < 0) {
9479                 perror("fork");
9480                 g_free(tmp);
9481                 return;
9482         }
9483
9484         if (pid != 0) {
9485                 /* close the write side of the pipe */
9486                 close(pipe_fds[1]);
9487
9488                 compose->exteditor_file    = g_strdup(tmp);
9489                 compose->exteditor_pid     = pid;
9490
9491                 compose_set_ext_editor_sensitive(compose, FALSE);
9492
9493 #ifndef G_OS_WIN32
9494                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9495 #else
9496                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9497 #endif
9498                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9499                                                         G_IO_IN,
9500                                                         compose_input_cb,
9501                                                         compose);
9502         } else {        /* process-monitoring process */
9503                 pid_t pid_ed;
9504
9505                 if (setpgid(0, 0))
9506                         perror("setpgid");
9507
9508                 /* close the read side of the pipe */
9509                 close(pipe_fds[0]);
9510
9511                 if (compose_write_body_to_file(compose, tmp) < 0) {
9512                         fd_write_all(pipe_fds[1], "2\n", 2);
9513                         _exit(1);
9514                 }
9515
9516                 pid_ed = compose_exec_ext_editor_real(tmp, socket_wid);
9517                 if (pid_ed < 0) {
9518                         fd_write_all(pipe_fds[1], "1\n", 2);
9519                         _exit(1);
9520                 }
9521
9522                 /* wait until editor is terminated */
9523                 waitpid(pid_ed, NULL, 0);
9524
9525                 fd_write_all(pipe_fds[1], "0\n", 2);
9526
9527                 close(pipe_fds[1]);
9528                 _exit(0);
9529         }
9530
9531         g_free(tmp);
9532 #endif /* G_OS_UNIX */
9533 }
9534
9535 static gboolean compose_can_autosave(Compose *compose)
9536 {
9537         if (compose->privacy_system && compose->use_encryption)
9538                 return prefs_common.autosave && prefs_common.autosave_encrypted;
9539         else
9540                 return prefs_common.autosave;
9541 }
9542
9543 #ifdef G_OS_UNIX
9544 static gboolean compose_get_ext_editor_cmd_valid()
9545 {
9546         gboolean has_s = FALSE;
9547         gboolean has_w = FALSE;
9548         const gchar *p = prefs_common_get_ext_editor_cmd();
9549         if (!p)
9550                 return FALSE;
9551         while ((p = strchr(p, '%'))) {
9552                 p++;
9553                 if (*p == 's') {
9554                         if (has_s)
9555                                 return FALSE;
9556                         has_s = TRUE;
9557                 } else if (*p == 'w') {
9558                         if (has_w)
9559                                 return FALSE;
9560                         has_w = TRUE;
9561                 } else {
9562                         return FALSE;
9563                 }
9564         }
9565         return TRUE;
9566 }
9567
9568 static gint compose_exec_ext_editor_real(const gchar *file, GdkNativeWindow socket_wid)
9569 {
9570         gchar *buf;
9571         gchar *p, *s;
9572         gchar **cmdline;
9573         pid_t pid;
9574
9575         cm_return_val_if_fail(file != NULL, -1);
9576
9577         if ((pid = fork()) < 0) {
9578                 perror("fork");
9579                 return -1;
9580         }
9581
9582         if (pid != 0) return pid;
9583
9584         /* grandchild process */
9585
9586         if (setpgid(0, getppid()))
9587                 perror("setpgid");
9588
9589         if (compose_get_ext_editor_cmd_valid()) {
9590                 if (compose_get_ext_editor_uses_socket()) {
9591                         p = g_strdup(prefs_common_get_ext_editor_cmd());
9592                         s = strstr(p, "%w");
9593                         s[1] = 'u';
9594                         if (strstr(p, "%s") < s)
9595                                 buf = g_strdup_printf(p, file, socket_wid);
9596                         else
9597                                 buf = g_strdup_printf(p, socket_wid, file);
9598                         g_free(p);
9599                 } else {
9600                         buf = g_strdup_printf(prefs_common_get_ext_editor_cmd(), file);
9601                 }
9602         } else {
9603                 if (prefs_common_get_ext_editor_cmd())
9604                         g_warning("External editor command-line is invalid: '%s'",
9605                                   prefs_common_get_ext_editor_cmd());
9606                 buf = g_strdup_printf(DEFAULT_EDITOR_CMD, file);
9607         }
9608
9609         cmdline = strsplit_with_quote(buf, " ", 0);
9610         g_free(buf);
9611         execvp(cmdline[0], cmdline);
9612
9613         perror("execvp");
9614         g_strfreev(cmdline);
9615
9616         _exit(1);
9617 }
9618
9619 static gboolean compose_ext_editor_kill(Compose *compose)
9620 {
9621         pid_t pgid = compose->exteditor_pid * -1;
9622         gint ret;
9623
9624         ret = kill(pgid, 0);
9625
9626         if (ret == 0 || (ret == -1 && EPERM == errno)) {
9627                 AlertValue val;
9628                 gchar *msg;
9629
9630                 msg = g_strdup_printf
9631                         (_("The external editor is still working.\n"
9632                            "Force terminating the process?\n"
9633                            "process group id: %d"), -pgid);
9634                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9635                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9636                         
9637                 g_free(msg);
9638
9639                 if (val == G_ALERTALTERNATE) {
9640                         g_source_remove(compose->exteditor_tag);
9641                         g_io_channel_shutdown(compose->exteditor_ch,
9642                                               FALSE, NULL);
9643                         g_io_channel_unref(compose->exteditor_ch);
9644
9645                         if (kill(pgid, SIGTERM) < 0) perror("kill");
9646                         waitpid(compose->exteditor_pid, NULL, 0);
9647
9648                         g_warning("Terminated process group id: %d. "
9649                                   "Temporary file: %s", -pgid, compose->exteditor_file);
9650
9651                         compose_set_ext_editor_sensitive(compose, TRUE);
9652
9653                         g_free(compose->exteditor_file);
9654                         compose->exteditor_file    = NULL;
9655                         compose->exteditor_pid     = -1;
9656                         compose->exteditor_ch      = NULL;
9657                         compose->exteditor_tag     = -1;
9658                 } else
9659                         return FALSE;
9660         }
9661
9662         return TRUE;
9663 }
9664
9665 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9666                                  gpointer data)
9667 {
9668         gchar buf[3] = "3";
9669         Compose *compose = (Compose *)data;
9670         gsize bytes_read;
9671
9672         debug_print("Compose: input from monitoring process\n");
9673
9674         if (g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL) != G_IO_STATUS_NORMAL) {
9675                 bytes_read = 0;
9676                 buf[0] = '\0';
9677         }
9678
9679         g_io_channel_shutdown(source, FALSE, NULL);
9680         g_io_channel_unref(source);
9681
9682         waitpid(compose->exteditor_pid, NULL, 0);
9683
9684         if (buf[0] == '0') {            /* success */
9685                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9686                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9687                 GtkTextIter start, end;
9688                 gchar *chars;
9689
9690                 gtk_text_buffer_set_text(buffer, "", -1);
9691                 compose_insert_file(compose, compose->exteditor_file);
9692                 compose_changed_cb(NULL, compose);
9693
9694                 /* Check if we should save the draft or not */
9695                 if (compose_can_autosave(compose))
9696                   compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9697
9698                 if (claws_unlink(compose->exteditor_file) < 0)
9699                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9700
9701                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
9702                 gtk_text_buffer_get_start_iter(buffer, &start);
9703                 gtk_text_buffer_get_end_iter(buffer, &end);
9704                 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
9705                 if (chars && strlen(chars) > 0)
9706                         compose->modified = TRUE;
9707                 g_free(chars);
9708         } else if (buf[0] == '1') {     /* failed */
9709                 g_warning("Couldn't exec external editor");
9710                 if (claws_unlink(compose->exteditor_file) < 0)
9711                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9712         } else if (buf[0] == '2') {
9713                 g_warning("Couldn't write to file");
9714         } else if (buf[0] == '3') {
9715                 g_warning("Pipe read failed");
9716         }
9717
9718         compose_set_ext_editor_sensitive(compose, TRUE);
9719
9720         g_free(compose->exteditor_file);
9721         compose->exteditor_file    = NULL;
9722         compose->exteditor_pid     = -1;
9723         compose->exteditor_ch      = NULL;
9724         compose->exteditor_tag     = -1;
9725         if (compose->exteditor_socket) {
9726                 gtk_widget_destroy(compose->exteditor_socket);
9727                 compose->exteditor_socket = NULL;
9728         }
9729
9730
9731         return FALSE;
9732 }
9733
9734 static char *ext_editor_menu_entries[] = {
9735         "Menu/Message/Send",
9736         "Menu/Message/SendLater",
9737         "Menu/Message/InsertFile",
9738         "Menu/Message/InsertSig",
9739         "Menu/Message/ReplaceSig",
9740         "Menu/Message/Save",
9741         "Menu/Message/Print",
9742         "Menu/Edit",
9743 #if USE_ENCHANT
9744         "Menu/Spelling",
9745 #endif
9746         "Menu/Tools/ShowRuler",
9747         "Menu/Tools/Actions",
9748         "Menu/Help",
9749         NULL
9750 };
9751
9752 static void compose_set_ext_editor_sensitive(Compose *compose,
9753                                              gboolean sensitive)
9754 {
9755         int i;
9756
9757         for (i = 0; ext_editor_menu_entries[i]; ++i) {
9758                 cm_menu_set_sensitive_full(compose->ui_manager,
9759                         ext_editor_menu_entries[i], sensitive);
9760         }
9761
9762         if (compose_get_ext_editor_uses_socket()) {
9763                 if (sensitive) {
9764                         if (compose->exteditor_socket)
9765                                 gtk_widget_hide(compose->exteditor_socket);
9766                         gtk_widget_show(compose->scrolledwin);
9767                         if (prefs_common.show_ruler)
9768                                 gtk_widget_show(compose->ruler_hbox);
9769                         /* Fix the focus, as it doesn't go anywhere when the
9770                          * socket is hidden or destroyed */
9771                         gtk_widget_child_focus(compose->window, GTK_DIR_TAB_BACKWARD);
9772                 } else {
9773                         g_assert (compose->exteditor_socket != NULL);
9774                         /* Fix the focus, as it doesn't go anywhere when the
9775                          * edit box is hidden */
9776                         if (gtk_widget_is_focus(compose->text))
9777                                 gtk_widget_child_focus(compose->window, GTK_DIR_TAB_BACKWARD);
9778                         gtk_widget_hide(compose->scrolledwin);
9779                         gtk_widget_hide(compose->ruler_hbox);
9780                         gtk_widget_show(compose->exteditor_socket);
9781                 }
9782         } else {
9783                 gtk_widget_set_sensitive(compose->text,                   sensitive);
9784         }
9785         if (compose->toolbar->send_btn)
9786                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
9787         if (compose->toolbar->sendl_btn)
9788                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
9789         if (compose->toolbar->draft_btn)
9790                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
9791         if (compose->toolbar->insert_btn)
9792                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
9793         if (compose->toolbar->sig_btn)
9794                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
9795         if (compose->toolbar->exteditor_btn)
9796                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9797         if (compose->toolbar->linewrap_current_btn)
9798                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9799         if (compose->toolbar->linewrap_all_btn)
9800                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9801 }
9802
9803 static gboolean compose_get_ext_editor_uses_socket()
9804 {
9805         return (prefs_common_get_ext_editor_cmd() &&
9806                 strstr(prefs_common_get_ext_editor_cmd(), "%w"));
9807 }
9808
9809 static gboolean compose_ext_editor_plug_removed_cb(GtkSocket *socket, Compose *compose)
9810 {
9811         compose->exteditor_socket = NULL;
9812         /* returning FALSE allows destruction of the socket */
9813         return FALSE;
9814 }
9815 #endif /* G_OS_UNIX */
9816
9817 /**
9818  * compose_undo_state_changed:
9819  *
9820  * Change the sensivity of the menuentries undo and redo
9821  **/
9822 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9823                                        gint redo_state, gpointer data)
9824 {
9825         Compose *compose = (Compose *)data;
9826
9827         switch (undo_state) {
9828         case UNDO_STATE_TRUE:
9829                 if (!undostruct->undo_state) {
9830                         undostruct->undo_state = TRUE;
9831                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9832                 }
9833                 break;
9834         case UNDO_STATE_FALSE:
9835                 if (undostruct->undo_state) {
9836                         undostruct->undo_state = FALSE;
9837                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9838                 }
9839                 break;
9840         case UNDO_STATE_UNCHANGED:
9841                 break;
9842         case UNDO_STATE_REFRESH:
9843                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9844                 break;
9845         default:
9846                 g_warning("Undo state not recognized");
9847                 break;
9848         }
9849
9850         switch (redo_state) {
9851         case UNDO_STATE_TRUE:
9852                 if (!undostruct->redo_state) {
9853                         undostruct->redo_state = TRUE;
9854                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9855                 }
9856                 break;
9857         case UNDO_STATE_FALSE:
9858                 if (undostruct->redo_state) {
9859                         undostruct->redo_state = FALSE;
9860                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9861                 }
9862                 break;
9863         case UNDO_STATE_UNCHANGED:
9864                 break;
9865         case UNDO_STATE_REFRESH:
9866                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9867                 break;
9868         default:
9869                 g_warning("Redo state not recognized");
9870                 break;
9871         }
9872 }
9873
9874 /* callback functions */
9875
9876 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9877                                         GtkAllocation *allocation,
9878                                         GtkPaned *paned)
9879 {
9880         prefs_common.compose_notebook_height = gtk_paned_get_position(paned);
9881 }
9882
9883 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9884  * includes "non-client" (windows-izm) in calculation, so this calculation
9885  * may not be accurate.
9886  */
9887 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9888                                         GtkAllocation *allocation,
9889                                         GtkSHRuler *shruler)
9890 {
9891         if (prefs_common.show_ruler) {
9892                 gint char_width = 0, char_height = 0;
9893                 gint line_width_in_chars;
9894
9895                 gtkut_get_font_size(GTK_WIDGET(widget),
9896                                     &char_width, &char_height);
9897                 line_width_in_chars =
9898                         (allocation->width - allocation->x) / char_width;
9899
9900                 /* got the maximum */
9901                 gtk_shruler_set_range(GTK_SHRULER(shruler),
9902                                     0.0, line_width_in_chars, 0);
9903         }
9904
9905         return TRUE;
9906 }
9907
9908 typedef struct {
9909         gchar                   *header;
9910         gchar                   *entry;
9911         ComposePrefType         type;
9912         gboolean                entry_marked;
9913 } HeaderEntryState;
9914
9915 static void account_activated(GtkComboBox *optmenu, gpointer data)
9916 {
9917         Compose *compose = (Compose *)data;
9918
9919         PrefsAccount *ac;
9920         gchar *folderidentifier;
9921         gint account_id = 0;
9922         GtkTreeModel *menu;
9923         GtkTreeIter iter;
9924         GSList *list, *saved_list = NULL;
9925         HeaderEntryState *state;
9926
9927         /* Get ID of active account in the combo box */
9928         menu = gtk_combo_box_get_model(optmenu);
9929         cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu, &iter));
9930         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9931
9932         ac = account_find_from_id(account_id);
9933         cm_return_if_fail(ac != NULL);
9934
9935         if (ac != compose->account) {
9936                 compose_select_account(compose, ac, FALSE);
9937
9938                 for (list = compose->header_list; list; list = list->next) {
9939                         ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9940                         
9941                         if (hentry->type == PREF_ACCOUNT || !list->next) {
9942                                 compose_destroy_headerentry(compose, hentry);
9943                                 continue;
9944                         }
9945                         state = g_malloc0(sizeof(HeaderEntryState));
9946                         state->header = gtk_editable_get_chars(GTK_EDITABLE(
9947                                         gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9948                         state->entry = gtk_editable_get_chars(
9949                                         GTK_EDITABLE(hentry->entry), 0, -1);
9950                         state->type = hentry->type;
9951
9952                         saved_list = g_slist_append(saved_list, state);
9953                         compose_destroy_headerentry(compose, hentry);
9954                 }
9955
9956                 compose->header_last = NULL;
9957                 g_slist_free(compose->header_list);
9958                 compose->header_list = NULL;
9959                 compose->header_nextrow = 1;
9960                 compose_create_header_entry(compose);
9961                 
9962                 if (ac->set_autocc && ac->auto_cc)
9963                         compose_entry_append(compose, ac->auto_cc,
9964                                                 COMPOSE_CC, PREF_ACCOUNT);
9965                 if (ac->set_autobcc && ac->auto_bcc)
9966                         compose_entry_append(compose, ac->auto_bcc,
9967                                                 COMPOSE_BCC, PREF_ACCOUNT);
9968                 if (ac->set_autoreplyto && ac->auto_replyto)
9969                         compose_entry_append(compose, ac->auto_replyto,
9970                                                 COMPOSE_REPLYTO, PREF_ACCOUNT);
9971                 
9972                 for (list = saved_list; list; list = list->next) {
9973                         state = (HeaderEntryState *) list->data;
9974
9975                         compose_add_header_entry(compose, state->header,
9976                                                 state->entry, state->type);
9977
9978                         g_free(state->header);
9979                         g_free(state->entry);
9980                         g_free(state);
9981                 }
9982                 g_slist_free(saved_list);
9983
9984                 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9985                                         (ac->protocol == A_NNTP) ? 
9986                                         COMPOSE_NEWSGROUPS : COMPOSE_TO);
9987         }
9988
9989         /* Set message save folder */
9990         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9991                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9992         }
9993         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9994                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9995                            
9996         compose_set_save_to(compose, NULL);
9997         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9998                 folderidentifier = folder_item_get_identifier(account_get_special_folder
9999                                   (compose->account, F_OUTBOX));
10000                 compose_set_save_to(compose, folderidentifier);
10001                 g_free(folderidentifier);
10002         }
10003 }
10004
10005 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
10006                             GtkTreeViewColumn *column, Compose *compose)
10007 {
10008         compose_attach_property(NULL, compose);
10009 }
10010
10011 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
10012                                       gpointer data)
10013 {
10014         Compose *compose = (Compose *)data;
10015         GtkTreeSelection *attach_selection;
10016         gint attach_nr_selected;
10017         GtkTreePath *path;
10018         
10019         if (!event) return FALSE;
10020
10021         if (event->button == 3) {
10022                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
10023                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
10024
10025                 /* If no rows, or just one row is selected, right-click should
10026                  * open menu relevant to the row being right-clicked on. We
10027                  * achieve that by selecting the clicked row first. If more
10028                  * than one row is selected, we shouldn't modify the selection,
10029                  * as user may want to remove selected rows (attachments). */
10030                 if (attach_nr_selected < 2) {
10031                         gtk_tree_selection_unselect_all(attach_selection);
10032                         attach_nr_selected = 0;
10033                         gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
10034                                         event->x, event->y, &path, NULL, NULL, NULL);
10035                         if (path != NULL) {
10036                                 gtk_tree_selection_select_path(attach_selection, path);
10037                                 gtk_tree_path_free(path);
10038                                 attach_nr_selected++;
10039                         }
10040                 }
10041
10042                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
10043                 /* Properties menu item makes no sense with more than one row
10044                  * selected, the properties dialog can only edit one attachment. */
10045                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected == 1));
10046                         
10047                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
10048                                NULL, NULL, event->button, event->time);
10049                 return TRUE;                           
10050         }
10051
10052         return FALSE;
10053 }
10054
10055 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
10056                                    gpointer data)
10057 {
10058         Compose *compose = (Compose *)data;
10059
10060         if (!event) return FALSE;
10061
10062         switch (event->keyval) {
10063         case GDK_KEY_Delete:
10064                 compose_attach_remove_selected(NULL, compose);
10065                 break;
10066         }
10067         return FALSE;
10068 }
10069
10070 static void compose_allow_user_actions (Compose *compose, gboolean allow)
10071 {
10072         toolbar_comp_set_sensitive(compose, allow);
10073         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
10074         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
10075 #if USE_ENCHANT
10076         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
10077 #endif  
10078         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
10079         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
10080         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
10081         
10082         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
10083
10084 }
10085
10086 static void compose_send_cb(GtkAction *action, gpointer data)
10087 {
10088         Compose *compose = (Compose *)data;
10089
10090 #ifdef G_OS_UNIX
10091         if (compose->exteditor_tag != -1) {
10092                 debug_print("ignoring send: external editor still open\n");
10093                 return;
10094         }
10095 #endif
10096         if (prefs_common.work_offline && 
10097             !inc_offline_should_override(TRUE,
10098                 _("Claws Mail needs network access in order "
10099                   "to send this email.")))
10100                 return;
10101         
10102         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
10103                 g_source_remove(compose->draft_timeout_tag);
10104                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
10105         }
10106
10107         compose_send(compose);
10108 }
10109
10110 static void compose_send_later_cb(GtkAction *action, gpointer data)
10111 {
10112         Compose *compose = (Compose *)data;
10113         gint val;
10114
10115         inc_lock();
10116         compose_allow_user_actions(compose, FALSE);
10117         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
10118         compose_allow_user_actions(compose, TRUE);
10119         inc_unlock();
10120
10121         if (!val) {
10122                 compose_close(compose);
10123         } else if (val == -1) {
10124                 alertpanel_error(_("Could not queue message."));
10125         } else if (val == -2) {
10126                 alertpanel_error(_("Could not queue message:\n\n%s."), g_strerror(errno));
10127         } else if (val == -3) {
10128                 if (privacy_peek_error())
10129                 alertpanel_error(_("Could not queue message for sending:\n\n"
10130                                    "Signature failed: %s"), privacy_get_error());
10131         } else if (val == -4) {
10132                 alertpanel_error(_("Could not queue message for sending:\n\n"
10133                                    "Charset conversion failed."));
10134         } else if (val == -5) {
10135                 alertpanel_error(_("Could not queue message for sending:\n\n"
10136                                    "Couldn't get recipient encryption key."));
10137         } else if (val == -6) {
10138                 /* silent error */
10139         }
10140         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
10141 }
10142
10143 #define DRAFTED_AT_EXIT "drafted_at_exit"
10144 static void compose_register_draft(MsgInfo *info)
10145 {
10146         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10147                                       DRAFTED_AT_EXIT, NULL);
10148         FILE *fp = g_fopen(filepath, "ab");
10149         
10150         if (fp) {
10151                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
10152                                 info->msgnum);
10153                 fclose(fp);
10154         }
10155                 
10156         g_free(filepath);       
10157 }
10158
10159 gboolean compose_draft (gpointer data, guint action) 
10160 {
10161         Compose *compose = (Compose *)data;
10162         FolderItem *draft;
10163         gchar *tmp;
10164         gchar *sheaders;
10165         gint msgnum;
10166         MsgFlags flag = {0, 0};
10167         static gboolean lock = FALSE;
10168         MsgInfo *newmsginfo;
10169         FILE *fp;
10170         gboolean target_locked = FALSE;
10171         gboolean err = FALSE;
10172
10173         if (lock) return FALSE;
10174
10175         if (compose->sending)
10176                 return TRUE;
10177
10178         draft = account_get_special_folder(compose->account, F_DRAFT);
10179         cm_return_val_if_fail(draft != NULL, FALSE);
10180         
10181         if (!g_mutex_trylock(compose->mutex)) {
10182                 /* we don't want to lock the mutex once it's available,
10183                  * because as the only other part of compose.c locking
10184                  * it is compose_close - which means once unlocked,
10185                  * the compose struct will be freed */
10186                 debug_print("couldn't lock mutex, probably sending\n");
10187                 return FALSE;
10188         }
10189
10190         lock = TRUE;
10191
10192         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
10193                               G_DIR_SEPARATOR, compose);
10194         if ((fp = g_fopen(tmp, "wb")) == NULL) {
10195                 FILE_OP_ERROR(tmp, "fopen");
10196                 goto warn_err;
10197         }
10198
10199         /* chmod for security */
10200         if (change_file_mode_rw(fp, tmp) < 0) {
10201                 FILE_OP_ERROR(tmp, "chmod");
10202                 g_warning("can't change file mode");
10203         }
10204
10205         /* Save draft infos */
10206         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
10207         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
10208
10209         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
10210                 gchar *savefolderid;
10211
10212                 savefolderid = compose_get_save_to(compose);
10213                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
10214                 g_free(savefolderid);
10215         }
10216         if (compose->return_receipt) {
10217                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
10218         }
10219         if (compose->privacy_system) {
10220                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
10221                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
10222                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
10223         }
10224
10225         /* Message-ID of message replying to */
10226         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
10227                 gchar *folderid = NULL;
10228
10229                 if (compose->replyinfo->folder)
10230                         folderid = folder_item_get_identifier(compose->replyinfo->folder);
10231                 if (folderid == NULL)
10232                         folderid = g_strdup("NULL");
10233
10234                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
10235                 g_free(folderid);
10236         }
10237         /* Message-ID of message forwarding to */
10238         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
10239                 gchar *folderid = NULL;
10240
10241                 if (compose->fwdinfo->folder)
10242                         folderid = folder_item_get_identifier(compose->fwdinfo->folder);
10243                 if (folderid == NULL)
10244                         folderid = g_strdup("NULL");
10245
10246                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
10247                 g_free(folderid);
10248         }
10249
10250         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
10251         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
10252
10253         sheaders = compose_get_manual_headers_info(compose);
10254         err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
10255         g_free(sheaders);
10256
10257         /* end of headers */
10258         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
10259
10260         if (err) {
10261                 fclose(fp);
10262                 goto warn_err;
10263         }
10264
10265         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
10266                 fclose(fp);
10267                 goto warn_err;
10268         }
10269         if (fclose(fp) == EOF) {
10270                 goto warn_err;
10271         }
10272         
10273         flag.perm_flags = MSG_NEW|MSG_UNREAD;
10274         if (compose->targetinfo) {
10275                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
10276                 if (target_locked) 
10277                         flag.perm_flags |= MSG_LOCKED;
10278         }
10279         flag.tmp_flags = MSG_DRAFT;
10280
10281         folder_item_scan(draft);
10282         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
10283                 MsgInfo *tmpinfo = NULL;
10284                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
10285                 if (compose->msgid) {
10286                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
10287                 }
10288                 if (tmpinfo) {
10289                         msgnum = tmpinfo->msgnum;
10290                         procmsg_msginfo_free(&tmpinfo);
10291                         debug_print("got draft msgnum %d from scanning\n", msgnum);
10292                 } else {
10293                         debug_print("didn't get draft msgnum after scanning\n");
10294                 }
10295         } else {
10296                 debug_print("got draft msgnum %d from adding\n", msgnum);
10297         }
10298         if (msgnum < 0) {
10299 warn_err:
10300                 claws_unlink(tmp);
10301                 g_free(tmp);
10302                 if (action != COMPOSE_AUTO_SAVE) {
10303                         if (action != COMPOSE_DRAFT_FOR_EXIT)
10304                                 alertpanel_error(_("Could not save draft."));
10305                         else {
10306                                 AlertValue val;
10307                                 gtkut_window_popup(compose->window);
10308                                 val = alertpanel_full(_("Could not save draft"),
10309                                         _("Could not save draft.\n"
10310                                         "Do you want to cancel exit or discard this email?"),
10311                                           _("_Cancel exit"), _("_Discard email"), NULL,
10312                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
10313                                 if (val == G_ALERTALTERNATE) {
10314                                         lock = FALSE;
10315                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
10316                                         compose_close(compose);
10317                                         return TRUE;
10318                                 } else {
10319                                         lock = FALSE;
10320                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
10321                                         return FALSE;
10322                                 }
10323                         }
10324                 }
10325                 goto unlock;
10326         }
10327         g_free(tmp);
10328
10329         if (compose->mode == COMPOSE_REEDIT) {
10330                 compose_remove_reedit_target(compose, TRUE);
10331         }
10332
10333         newmsginfo = folder_item_get_msginfo(draft, msgnum);
10334
10335         if (newmsginfo) {
10336                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
10337                 if (target_locked)
10338                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD|MSG_LOCKED, MSG_DRAFT);
10339                 else
10340                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD, MSG_DRAFT);
10341                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
10342                         procmsg_msginfo_set_flags(newmsginfo, 0,
10343                                                   MSG_HAS_ATTACHMENT);
10344
10345                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
10346                         compose_register_draft(newmsginfo);
10347                 }
10348                 procmsg_msginfo_free(&newmsginfo);
10349         }
10350         
10351         folder_item_scan(draft);
10352         
10353         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
10354                 lock = FALSE;
10355                 g_mutex_unlock(compose->mutex); /* must be done before closing */
10356                 compose_close(compose);
10357                 return TRUE;
10358         } else {
10359                 GStatBuf s;
10360                 gchar *path;
10361
10362                 path = folder_item_fetch_msg(draft, msgnum);
10363                 if (path == NULL) {
10364                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
10365                         goto unlock;
10366                 }
10367                 if (g_stat(path, &s) < 0) {
10368                         FILE_OP_ERROR(path, "stat");
10369                         g_free(path);
10370                         goto unlock;
10371                 }
10372                 g_free(path);
10373
10374                 procmsg_msginfo_free(&(compose->targetinfo));
10375                 compose->targetinfo = procmsg_msginfo_new();
10376                 compose->targetinfo->msgnum = msgnum;
10377                 compose->targetinfo->size = (goffset)s.st_size;
10378                 compose->targetinfo->mtime = s.st_mtime;
10379                 compose->targetinfo->folder = draft;
10380                 if (target_locked)
10381                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
10382                 compose->mode = COMPOSE_REEDIT;
10383                 
10384                 if (action == COMPOSE_AUTO_SAVE) {
10385                         compose->autosaved_draft = compose->targetinfo;
10386                 }
10387                 compose->modified = FALSE;
10388                 compose_set_title(compose);
10389         }
10390 unlock:
10391         lock = FALSE;
10392         g_mutex_unlock(compose->mutex);
10393         return TRUE;
10394 }
10395
10396 void compose_clear_exit_drafts(void)
10397 {
10398         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10399                                       DRAFTED_AT_EXIT, NULL);
10400         if (is_file_exist(filepath))
10401                 claws_unlink(filepath);
10402         
10403         g_free(filepath);
10404 }
10405
10406 void compose_reopen_exit_drafts(void)
10407 {
10408         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10409                                       DRAFTED_AT_EXIT, NULL);
10410         FILE *fp = g_fopen(filepath, "rb");
10411         gchar buf[1024];
10412         
10413         if (fp) {
10414                 while (fgets(buf, sizeof(buf), fp)) {
10415                         gchar **parts = g_strsplit(buf, "\t", 2);
10416                         const gchar *folder = parts[0];
10417                         int msgnum = parts[1] ? atoi(parts[1]):-1;
10418                         
10419                         if (folder && *folder && msgnum > -1) {
10420                                 FolderItem *item = folder_find_item_from_identifier(folder);
10421                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
10422                                 if (info)
10423                                         compose_reedit(info, FALSE);
10424                         }
10425                         g_strfreev(parts);
10426                 }       
10427                 fclose(fp);
10428         }       
10429         g_free(filepath);
10430         compose_clear_exit_drafts();
10431 }
10432
10433 static void compose_save_cb(GtkAction *action, gpointer data)
10434 {
10435         Compose *compose = (Compose *)data;
10436         compose_draft(compose, COMPOSE_KEEP_EDITING);
10437         compose->rmode = COMPOSE_REEDIT;
10438 }
10439
10440 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
10441 {
10442         if (compose && file_list) {
10443                 GList *tmp;
10444
10445                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10446                         gchar *file = (gchar *) tmp->data;
10447                         gchar *utf8_filename = conv_filename_to_utf8(file);
10448                         compose_attach_append(compose, file, utf8_filename, NULL, NULL);
10449                         compose_changed_cb(NULL, compose);
10450                         if (free_data) {
10451                         g_free(file);
10452                                 tmp->data = NULL;
10453                         }
10454                         g_free(utf8_filename);
10455                 }
10456         }
10457 }
10458
10459 static void compose_attach_cb(GtkAction *action, gpointer data)
10460 {
10461         Compose *compose = (Compose *)data;
10462         GList *file_list;
10463
10464         if (compose->redirect_filename != NULL)
10465                 return;
10466
10467         /* Set focus_window properly, in case we were called via popup menu,
10468          * which unsets it (via focus_out_event callback on compose window). */
10469         manage_window_focus_in(compose->window, NULL, NULL);
10470
10471         file_list = filesel_select_multiple_files_open(_("Select file"), NULL);
10472
10473         if (file_list) {
10474                 compose_attach_from_list(compose, file_list, TRUE);
10475                 g_list_free(file_list);
10476         }
10477 }
10478
10479 static void compose_insert_file_cb(GtkAction *action, gpointer data)
10480 {
10481         Compose *compose = (Compose *)data;
10482         GList *file_list;
10483         gint files_inserted = 0;
10484
10485         file_list = filesel_select_multiple_files_open(_("Select file"), NULL);
10486
10487         if (file_list) {
10488                 GList *tmp;
10489
10490                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10491                         gchar *file = (gchar *) tmp->data;
10492                         gchar *filedup = g_strdup(file);
10493                         gchar *shortfile = g_path_get_basename(filedup);
10494                         ComposeInsertResult res;
10495                         /* insert the file if the file is short or if the user confirmed that
10496                            he/she wants to insert the large file */
10497                         res = compose_insert_file(compose, file);
10498                         if (res == COMPOSE_INSERT_READ_ERROR) {
10499                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
10500                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
10501                                 alertpanel_error(_("File '%s' contained invalid characters\n"
10502                                                         "for the current encoding, insertion may be incorrect."),
10503                                                         shortfile);
10504                         } else if (res == COMPOSE_INSERT_SUCCESS)
10505                                 files_inserted++;
10506
10507                         g_free(shortfile);
10508                         g_free(filedup);
10509                         g_free(file);
10510                 }
10511                 g_list_free(file_list);
10512         }
10513
10514 #ifdef USE_ENCHANT      
10515         if (files_inserted > 0 && compose->gtkaspell && 
10516             compose->gtkaspell->check_while_typing)
10517                 gtkaspell_highlight_all(compose->gtkaspell);
10518 #endif
10519 }
10520
10521 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
10522 {
10523         Compose *compose = (Compose *)data;
10524
10525         compose_insert_sig(compose, FALSE);
10526 }
10527
10528 static void compose_replace_sig_cb(GtkAction *action, gpointer data)
10529 {
10530         Compose *compose = (Compose *)data;
10531
10532         compose_insert_sig(compose, TRUE);
10533 }
10534
10535 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
10536                               gpointer data)
10537 {
10538         gint x, y;
10539         Compose *compose = (Compose *)data;
10540
10541         gtkut_widget_get_uposition(widget, &x, &y);
10542         if (!compose->batch) {
10543                 prefs_common.compose_x = x;
10544                 prefs_common.compose_y = y;
10545         }
10546         if (compose->sending || compose->updating)
10547                 return TRUE;
10548         compose_close_cb(NULL, compose);
10549         return TRUE;
10550 }
10551
10552 void compose_close_toolbar(Compose *compose)
10553 {
10554         compose_close_cb(NULL, compose);
10555 }
10556
10557 static void compose_close_cb(GtkAction *action, gpointer data)
10558 {
10559         Compose *compose = (Compose *)data;
10560         AlertValue val;
10561
10562 #ifdef G_OS_UNIX
10563         if (compose->exteditor_tag != -1) {
10564                 if (!compose_ext_editor_kill(compose))
10565                         return;
10566         }
10567 #endif
10568
10569         if (compose->modified) {
10570                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
10571                 if (!g_mutex_trylock(compose->mutex)) {
10572                         /* we don't want to lock the mutex once it's available,
10573                          * because as the only other part of compose.c locking
10574                          * it is compose_close - which means once unlocked,
10575                          * the compose struct will be freed */
10576                         debug_print("couldn't lock mutex, probably sending\n");
10577                         return;
10578                 }
10579                 if (!reedit) {
10580                         val = alertpanel(_("Discard message"),
10581                                  _("This message has been modified. Discard it?"),
10582                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
10583                 } else {
10584                         val = alertpanel(_("Save changes"),
10585                                  _("This message has been modified. Save the latest changes?"),
10586                                  _("_Don't save"), g_strconcat("+", _("_Save to Drafts"), NULL),
10587                                 GTK_STOCK_CANCEL);
10588                 }
10589                 g_mutex_unlock(compose->mutex);
10590                 switch (val) {
10591                 case G_ALERTDEFAULT:
10592                         if (compose_can_autosave(compose) && !reedit)
10593                                 compose_remove_draft(compose);
10594                         break;
10595                 case G_ALERTALTERNATE:
10596                         compose_draft(data, COMPOSE_QUIT_EDITING);
10597                         return;
10598                 default:
10599                         return;
10600                 }
10601         }
10602
10603         compose_close(compose);
10604 }
10605
10606 static void compose_print_cb(GtkAction *action, gpointer data)
10607 {
10608         Compose *compose = (Compose *) data;
10609
10610         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10611         if (compose->targetinfo)
10612                 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10613 }
10614
10615 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10616 {
10617         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10618         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10619         Compose *compose = (Compose *) data;
10620
10621         if (active)
10622                 compose->out_encoding = (CharSet)value;
10623 }
10624
10625 static void compose_address_cb(GtkAction *action, gpointer data)
10626 {
10627         Compose *compose = (Compose *)data;
10628
10629 #ifndef USE_ALT_ADDRBOOK
10630         addressbook_open(compose);
10631 #else
10632         GError* error = NULL;
10633         addressbook_connect_signals(compose);
10634         addressbook_dbus_open(TRUE, &error);
10635         if (error) {
10636                 g_warning("%s", error->message);
10637                 g_error_free(error);
10638         }
10639 #endif
10640 }
10641
10642 static void about_show_cb(GtkAction *action, gpointer data)
10643 {
10644         about_show();
10645 }
10646
10647 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10648 {
10649         Compose *compose = (Compose *)data;
10650         Template *tmpl;
10651         gchar *msg;
10652         AlertValue val;
10653
10654         tmpl = g_object_get_data(G_OBJECT(widget), "template");
10655         cm_return_if_fail(tmpl != NULL);
10656
10657         msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
10658                               tmpl->name);
10659         val = alertpanel(_("Apply template"), msg,
10660                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10661         g_free(msg);
10662
10663         if (val == G_ALERTDEFAULT)
10664                 compose_template_apply(compose, tmpl, TRUE);
10665         else if (val == G_ALERTALTERNATE)
10666                 compose_template_apply(compose, tmpl, FALSE);
10667 }
10668
10669 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10670 {
10671         Compose *compose = (Compose *)data;
10672
10673 #ifdef G_OS_UNIX
10674         if (compose->exteditor_tag != -1) {
10675                 debug_print("ignoring open external editor: external editor still open\n");
10676                 return;
10677         }
10678 #endif
10679         compose_exec_ext_editor(compose);
10680 }
10681
10682 static void compose_undo_cb(GtkAction *action, gpointer data)
10683 {
10684         Compose *compose = (Compose *)data;
10685         gboolean prev_autowrap = compose->autowrap;
10686
10687         compose->autowrap = FALSE;
10688         undo_undo(compose->undostruct);
10689         compose->autowrap = prev_autowrap;
10690 }
10691
10692 static void compose_redo_cb(GtkAction *action, gpointer data)
10693 {
10694         Compose *compose = (Compose *)data;
10695         gboolean prev_autowrap = compose->autowrap;
10696         
10697         compose->autowrap = FALSE;
10698         undo_redo(compose->undostruct);
10699         compose->autowrap = prev_autowrap;
10700 }
10701
10702 static void entry_cut_clipboard(GtkWidget *entry)
10703 {
10704         if (GTK_IS_EDITABLE(entry))
10705                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10706         else if (GTK_IS_TEXT_VIEW(entry))
10707                 gtk_text_buffer_cut_clipboard(
10708                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10709                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10710                         TRUE);
10711 }
10712
10713 static void entry_copy_clipboard(GtkWidget *entry)
10714 {
10715         if (GTK_IS_EDITABLE(entry))
10716                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10717         else if (GTK_IS_TEXT_VIEW(entry))
10718                 gtk_text_buffer_copy_clipboard(
10719                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10720                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10721 }
10722
10723 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
10724                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10725 {
10726         if (GTK_IS_TEXT_VIEW(entry)) {
10727                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10728                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10729                 GtkTextIter start_iter, end_iter;
10730                 gint start, end;
10731                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10732
10733                 if (contents == NULL)
10734                         return;
10735         
10736                 /* we shouldn't delete the selection when middle-click-pasting, or we
10737                  * can't mid-click-paste our own selection */
10738                 if (clip != GDK_SELECTION_PRIMARY) {
10739                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10740                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10741                 }
10742                 
10743                 if (insert_place == NULL) {
10744                         /* if insert_place isn't specified, insert at the cursor.
10745                          * used for Ctrl-V pasting */
10746                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10747                         start = gtk_text_iter_get_offset(&start_iter);
10748                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10749                 } else {
10750                         /* if insert_place is specified, paste here.
10751                          * used for mid-click-pasting */
10752                         start = gtk_text_iter_get_offset(insert_place);
10753                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10754                         if (prefs_common.primary_paste_unselects)
10755                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10756                 }
10757                 
10758                 if (!wrap) {
10759                         /* paste unwrapped: mark the paste so it's not wrapped later */
10760                         end = start + strlen(contents);
10761                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10762                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10763                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10764                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10765                         /* rewrap paragraph now (after a mid-click-paste) */
10766                         mark_start = gtk_text_buffer_get_insert(buffer);
10767                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10768                         gtk_text_iter_backward_char(&start_iter);
10769                         compose_beautify_paragraph(compose, &start_iter, TRUE);
10770                 }
10771         } else if (GTK_IS_EDITABLE(entry))
10772                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10773
10774         compose->modified = TRUE;
10775 }
10776
10777 static void entry_allsel(GtkWidget *entry)
10778 {
10779         if (GTK_IS_EDITABLE(entry))
10780                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10781         else if (GTK_IS_TEXT_VIEW(entry)) {
10782                 GtkTextIter startiter, enditer;
10783                 GtkTextBuffer *textbuf;
10784
10785                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10786                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10787                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10788
10789                 gtk_text_buffer_move_mark_by_name(textbuf, 
10790                         "selection_bound", &startiter);
10791                 gtk_text_buffer_move_mark_by_name(textbuf, 
10792                         "insert", &enditer);
10793         }
10794 }
10795
10796 static void compose_cut_cb(GtkAction *action, gpointer data)
10797 {
10798         Compose *compose = (Compose *)data;
10799         if (compose->focused_editable 
10800 #ifndef GENERIC_UMPC
10801             && gtk_widget_has_focus(compose->focused_editable)
10802 #endif
10803             )
10804                 entry_cut_clipboard(compose->focused_editable);
10805 }
10806
10807 static void compose_copy_cb(GtkAction *action, gpointer data)
10808 {
10809         Compose *compose = (Compose *)data;
10810         if (compose->focused_editable 
10811 #ifndef GENERIC_UMPC
10812             && gtk_widget_has_focus(compose->focused_editable)
10813 #endif
10814             )
10815                 entry_copy_clipboard(compose->focused_editable);
10816 }
10817
10818 static void compose_paste_cb(GtkAction *action, gpointer data)
10819 {
10820         Compose *compose = (Compose *)data;
10821         gint prev_autowrap;
10822         GtkTextBuffer *buffer;
10823         BLOCK_WRAP();
10824         if (compose->focused_editable &&
10825 #ifndef GENERIC_UMPC
10826             gtk_widget_has_focus(compose->focused_editable)
10827 #endif
10828                 )
10829                 entry_paste_clipboard(compose, compose->focused_editable, 
10830                                 prefs_common.linewrap_pastes,
10831                                 GDK_SELECTION_CLIPBOARD, NULL);
10832         UNBLOCK_WRAP();
10833
10834 #ifdef USE_ENCHANT
10835         if (
10836 #ifndef GENERIC_UMPC
10837                 gtk_widget_has_focus(compose->text) &&
10838 #endif
10839             compose->gtkaspell && 
10840             compose->gtkaspell->check_while_typing)
10841                 gtkaspell_highlight_all(compose->gtkaspell);
10842 #endif
10843 }
10844
10845 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10846 {
10847         Compose *compose = (Compose *)data;
10848         gint wrap_quote = prefs_common.linewrap_quote;
10849         if (compose->focused_editable 
10850 #ifndef GENERIC_UMPC
10851             && gtk_widget_has_focus(compose->focused_editable)
10852 #endif
10853             ) {
10854                 /* let text_insert() (called directly or at a later time
10855                  * after the gtk_editable_paste_clipboard) know that 
10856                  * text is to be inserted as a quotation. implemented
10857                  * by using a simple refcount... */
10858                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10859                                                 G_OBJECT(compose->focused_editable),
10860                                                 "paste_as_quotation"));
10861                 g_object_set_data(G_OBJECT(compose->focused_editable),
10862                                     "paste_as_quotation",
10863                                     GINT_TO_POINTER(paste_as_quotation + 1));
10864                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10865                 entry_paste_clipboard(compose, compose->focused_editable, 
10866                                 prefs_common.linewrap_pastes,
10867                                 GDK_SELECTION_CLIPBOARD, NULL);
10868                 prefs_common.linewrap_quote = wrap_quote;
10869         }
10870 }
10871
10872 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10873 {
10874         Compose *compose = (Compose *)data;
10875         gint prev_autowrap;
10876         GtkTextBuffer *buffer;
10877         BLOCK_WRAP();
10878         if (compose->focused_editable 
10879 #ifndef GENERIC_UMPC
10880             && gtk_widget_has_focus(compose->focused_editable)
10881 #endif
10882             )
10883                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10884                         GDK_SELECTION_CLIPBOARD, NULL);
10885         UNBLOCK_WRAP();
10886
10887 #ifdef USE_ENCHANT
10888         if (
10889 #ifndef GENERIC_UMPC
10890                 gtk_widget_has_focus(compose->text) &&
10891 #endif
10892             compose->gtkaspell && 
10893             compose->gtkaspell->check_while_typing)
10894                 gtkaspell_highlight_all(compose->gtkaspell);
10895 #endif
10896 }
10897
10898 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10899 {
10900         Compose *compose = (Compose *)data;
10901         gint prev_autowrap;
10902         GtkTextBuffer *buffer;
10903         BLOCK_WRAP();
10904         if (compose->focused_editable 
10905 #ifndef GENERIC_UMPC
10906             && gtk_widget_has_focus(compose->focused_editable)
10907 #endif
10908             )
10909                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10910                         GDK_SELECTION_CLIPBOARD, NULL);
10911         UNBLOCK_WRAP();
10912
10913 #ifdef USE_ENCHANT
10914         if (
10915 #ifndef GENERIC_UMPC
10916                 gtk_widget_has_focus(compose->text) &&
10917 #endif
10918             compose->gtkaspell &&
10919             compose->gtkaspell->check_while_typing)
10920                 gtkaspell_highlight_all(compose->gtkaspell);
10921 #endif
10922 }
10923
10924 static void compose_allsel_cb(GtkAction *action, gpointer data)
10925 {
10926         Compose *compose = (Compose *)data;
10927         if (compose->focused_editable 
10928 #ifndef GENERIC_UMPC
10929             && gtk_widget_has_focus(compose->focused_editable)
10930 #endif
10931             )
10932                 entry_allsel(compose->focused_editable);
10933 }
10934
10935 static void textview_move_beginning_of_line (GtkTextView *text)
10936 {
10937         GtkTextBuffer *buffer;
10938         GtkTextMark *mark;
10939         GtkTextIter ins;
10940
10941         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10942
10943         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10944         mark = gtk_text_buffer_get_insert(buffer);
10945         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10946         gtk_text_iter_set_line_offset(&ins, 0);
10947         gtk_text_buffer_place_cursor(buffer, &ins);
10948 }
10949
10950 static void textview_move_forward_character (GtkTextView *text)
10951 {
10952         GtkTextBuffer *buffer;
10953         GtkTextMark *mark;
10954         GtkTextIter ins;
10955
10956         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10957
10958         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10959         mark = gtk_text_buffer_get_insert(buffer);
10960         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10961         if (gtk_text_iter_forward_cursor_position(&ins))
10962                 gtk_text_buffer_place_cursor(buffer, &ins);
10963 }
10964
10965 static void textview_move_backward_character (GtkTextView *text)
10966 {
10967         GtkTextBuffer *buffer;
10968         GtkTextMark *mark;
10969         GtkTextIter ins;
10970
10971         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10972
10973         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10974         mark = gtk_text_buffer_get_insert(buffer);
10975         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10976         if (gtk_text_iter_backward_cursor_position(&ins))
10977                 gtk_text_buffer_place_cursor(buffer, &ins);
10978 }
10979
10980 static void textview_move_forward_word (GtkTextView *text)
10981 {
10982         GtkTextBuffer *buffer;
10983         GtkTextMark *mark;
10984         GtkTextIter ins;
10985         gint count;
10986
10987         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10988
10989         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10990         mark = gtk_text_buffer_get_insert(buffer);
10991         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10992         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10993         if (gtk_text_iter_forward_word_ends(&ins, count)) {
10994                 gtk_text_iter_backward_word_start(&ins);
10995                 gtk_text_buffer_place_cursor(buffer, &ins);
10996         }
10997 }
10998
10999 static void textview_move_backward_word (GtkTextView *text)
11000 {
11001         GtkTextBuffer *buffer;
11002         GtkTextMark *mark;
11003         GtkTextIter ins;
11004
11005         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11006
11007         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11008         mark = gtk_text_buffer_get_insert(buffer);
11009         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11010         if (gtk_text_iter_backward_word_starts(&ins, 1))
11011                 gtk_text_buffer_place_cursor(buffer, &ins);
11012 }
11013
11014 static void textview_move_end_of_line (GtkTextView *text)
11015 {
11016         GtkTextBuffer *buffer;
11017         GtkTextMark *mark;
11018         GtkTextIter ins;
11019
11020         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11021
11022         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11023         mark = gtk_text_buffer_get_insert(buffer);
11024         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11025         if (gtk_text_iter_forward_to_line_end(&ins))
11026                 gtk_text_buffer_place_cursor(buffer, &ins);
11027 }
11028
11029 static void textview_move_next_line (GtkTextView *text)
11030 {
11031         GtkTextBuffer *buffer;
11032         GtkTextMark *mark;
11033         GtkTextIter ins;
11034         gint offset;
11035
11036         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11037
11038         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11039         mark = gtk_text_buffer_get_insert(buffer);
11040         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11041         offset = gtk_text_iter_get_line_offset(&ins);
11042         if (gtk_text_iter_forward_line(&ins)) {
11043                 gtk_text_iter_set_line_offset(&ins, offset);
11044                 gtk_text_buffer_place_cursor(buffer, &ins);
11045         }
11046 }
11047
11048 static void textview_move_previous_line (GtkTextView *text)
11049 {
11050         GtkTextBuffer *buffer;
11051         GtkTextMark *mark;
11052         GtkTextIter ins;
11053         gint offset;
11054
11055         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11056
11057         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11058         mark = gtk_text_buffer_get_insert(buffer);
11059         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11060         offset = gtk_text_iter_get_line_offset(&ins);
11061         if (gtk_text_iter_backward_line(&ins)) {
11062                 gtk_text_iter_set_line_offset(&ins, offset);
11063                 gtk_text_buffer_place_cursor(buffer, &ins);
11064         }
11065 }
11066
11067 static void textview_delete_forward_character (GtkTextView *text)
11068 {
11069         GtkTextBuffer *buffer;
11070         GtkTextMark *mark;
11071         GtkTextIter ins, end_iter;
11072
11073         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11074
11075         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11076         mark = gtk_text_buffer_get_insert(buffer);
11077         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11078         end_iter = ins;
11079         if (gtk_text_iter_forward_char(&end_iter)) {
11080                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
11081         }
11082 }
11083
11084 static void textview_delete_backward_character (GtkTextView *text)
11085 {
11086         GtkTextBuffer *buffer;
11087         GtkTextMark *mark;
11088         GtkTextIter ins, end_iter;
11089
11090         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11091
11092         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11093         mark = gtk_text_buffer_get_insert(buffer);
11094         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11095         end_iter = ins;
11096         if (gtk_text_iter_backward_char(&end_iter)) {
11097                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
11098         }
11099 }
11100
11101 static void textview_delete_forward_word (GtkTextView *text)
11102 {
11103         GtkTextBuffer *buffer;
11104         GtkTextMark *mark;
11105         GtkTextIter ins, end_iter;
11106
11107         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11108
11109         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11110         mark = gtk_text_buffer_get_insert(buffer);
11111         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11112         end_iter = ins;
11113         if (gtk_text_iter_forward_word_end(&end_iter)) {
11114                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
11115         }
11116 }
11117
11118 static void textview_delete_backward_word (GtkTextView *text)
11119 {
11120         GtkTextBuffer *buffer;
11121         GtkTextMark *mark;
11122         GtkTextIter ins, end_iter;
11123
11124         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11125
11126         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11127         mark = gtk_text_buffer_get_insert(buffer);
11128         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11129         end_iter = ins;
11130         if (gtk_text_iter_backward_word_start(&end_iter)) {
11131                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
11132         }
11133 }
11134
11135 static void textview_delete_line (GtkTextView *text)
11136 {
11137         GtkTextBuffer *buffer;
11138         GtkTextMark *mark;
11139         GtkTextIter ins, start_iter, end_iter;
11140
11141         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11142
11143         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11144         mark = gtk_text_buffer_get_insert(buffer);
11145         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11146
11147         start_iter = ins;
11148         gtk_text_iter_set_line_offset(&start_iter, 0);
11149
11150         end_iter = ins;
11151         if (gtk_text_iter_ends_line(&end_iter)){
11152                 if (!gtk_text_iter_forward_char(&end_iter))
11153                         gtk_text_iter_backward_char(&start_iter);
11154         }
11155         else 
11156                 gtk_text_iter_forward_to_line_end(&end_iter);
11157         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
11158 }
11159
11160 static void textview_delete_to_line_end (GtkTextView *text)
11161 {
11162         GtkTextBuffer *buffer;
11163         GtkTextMark *mark;
11164         GtkTextIter ins, end_iter;
11165
11166         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11167
11168         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11169         mark = gtk_text_buffer_get_insert(buffer);
11170         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11171         end_iter = ins;
11172         if (gtk_text_iter_ends_line(&end_iter))
11173                 gtk_text_iter_forward_char(&end_iter);
11174         else
11175                 gtk_text_iter_forward_to_line_end(&end_iter);
11176         gtk_text_buffer_delete(buffer, &ins, &end_iter);
11177 }
11178
11179 #define DO_ACTION(name, act) {                                          \
11180         if(!strcmp(name, a_name)) {                                     \
11181                 return act;                                             \
11182         }                                                               \
11183 }
11184 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
11185 {
11186         const gchar *a_name = gtk_action_get_name(action);
11187         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
11188         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
11189         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
11190         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
11191         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
11192         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
11193         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
11194         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
11195         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
11196         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
11197         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
11198         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
11199         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
11200         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
11201         return COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED;
11202 }
11203
11204 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
11205 {
11206         Compose *compose = (Compose *)data;
11207         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11208         ComposeCallAdvancedAction action = COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED;
11209         
11210         action = compose_call_advanced_action_from_path(gaction);
11211
11212         static struct {
11213                 void (*do_action) (GtkTextView *text);
11214         } action_table[] = {
11215                 {textview_move_beginning_of_line},
11216                 {textview_move_forward_character},
11217                 {textview_move_backward_character},
11218                 {textview_move_forward_word},
11219                 {textview_move_backward_word},
11220                 {textview_move_end_of_line},
11221                 {textview_move_next_line},
11222                 {textview_move_previous_line},
11223                 {textview_delete_forward_character},
11224                 {textview_delete_backward_character},
11225                 {textview_delete_forward_word},
11226                 {textview_delete_backward_word},
11227                 {textview_delete_line},
11228                 {textview_delete_to_line_end}
11229         };
11230
11231         if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
11232
11233         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
11234             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
11235                 if (action_table[action].do_action)
11236                         action_table[action].do_action(text);
11237                 else
11238                         g_warning("Not implemented yet.");
11239         }
11240 }
11241
11242 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
11243 {
11244         GtkAllocation allocation;
11245         GtkWidget *parent;
11246         gchar *str = NULL;
11247         
11248         if (GTK_IS_EDITABLE(widget)) {
11249                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
11250                 gtk_editable_set_position(GTK_EDITABLE(widget), 
11251                         strlen(str));
11252                 g_free(str);
11253                 if ((parent = gtk_widget_get_parent(widget))
11254                  && (parent = gtk_widget_get_parent(parent))
11255                  && (parent = gtk_widget_get_parent(parent))) {
11256                         if (GTK_IS_SCROLLED_WINDOW(parent)) {
11257                                 gtk_widget_get_allocation(widget, &allocation);
11258                                 gint y = allocation.y;
11259                                 gint height = allocation.height;
11260                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
11261                                         (GTK_SCROLLED_WINDOW(parent));
11262
11263                                 gfloat value = gtk_adjustment_get_value(shown);
11264                                 gfloat upper = gtk_adjustment_get_upper(shown);
11265                                 gfloat page_size = gtk_adjustment_get_page_size(shown);
11266                                 if (y < (int)value) {
11267                                         gtk_adjustment_set_value(shown, y - 1);
11268                                 }
11269                                 if ((y + height) > ((int)value + (int)page_size)) {
11270                                         if ((y - height - 1) < ((int)upper - (int)page_size)) {
11271                                                 gtk_adjustment_set_value(shown, 
11272                                                         y + height - (int)page_size - 1);
11273                                         } else {
11274                                                 gtk_adjustment_set_value(shown, 
11275                                                         (int)upper - (int)page_size - 1);
11276                                         }
11277                                 }
11278                         }
11279                 }
11280         }
11281
11282         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
11283                 compose->focused_editable = widget;
11284         
11285 #ifdef GENERIC_UMPC
11286         if (GTK_IS_TEXT_VIEW(widget) 
11287             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
11288                 g_object_ref(compose->notebook);
11289                 g_object_ref(compose->edit_vbox);
11290                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
11291                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
11292                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
11293                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
11294                 g_object_unref(compose->notebook);
11295                 g_object_unref(compose->edit_vbox);
11296                 g_signal_handlers_block_by_func(G_OBJECT(widget),
11297                                         G_CALLBACK(compose_grab_focus_cb),
11298                                         compose);
11299                 gtk_widget_grab_focus(widget);
11300                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
11301                                         G_CALLBACK(compose_grab_focus_cb),
11302                                         compose);
11303         } else if (!GTK_IS_TEXT_VIEW(widget) 
11304                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
11305                 g_object_ref(compose->notebook);
11306                 g_object_ref(compose->edit_vbox);
11307                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
11308                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
11309                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
11310                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
11311                 g_object_unref(compose->notebook);
11312                 g_object_unref(compose->edit_vbox);
11313                 g_signal_handlers_block_by_func(G_OBJECT(widget),
11314                                         G_CALLBACK(compose_grab_focus_cb),
11315                                         compose);
11316                 gtk_widget_grab_focus(widget);
11317                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
11318                                         G_CALLBACK(compose_grab_focus_cb),
11319                                         compose);
11320         }
11321 #endif
11322 }
11323
11324 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
11325 {
11326         compose->modified = TRUE;
11327 /*      compose_beautify_paragraph(compose, NULL, TRUE); */
11328 #ifndef GENERIC_UMPC
11329         compose_set_title(compose);
11330 #endif
11331 }
11332
11333 static void compose_wrap_cb(GtkAction *action, gpointer data)
11334 {
11335         Compose *compose = (Compose *)data;
11336         compose_beautify_paragraph(compose, NULL, TRUE);
11337 }
11338
11339 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
11340 {
11341         Compose *compose = (Compose *)data;
11342         compose_wrap_all_full(compose, TRUE);
11343 }
11344
11345 static void compose_find_cb(GtkAction *action, gpointer data)
11346 {
11347         Compose *compose = (Compose *)data;
11348
11349         message_search_compose(compose);
11350 }
11351
11352 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
11353                                          gpointer        data)
11354 {
11355         Compose *compose = (Compose *)data;
11356         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11357         if (compose->autowrap)
11358                 compose_wrap_all_full(compose, TRUE);
11359         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11360 }
11361
11362 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
11363                                          gpointer        data)
11364 {
11365         Compose *compose = (Compose *)data;
11366         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11367 }
11368
11369 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
11370 {
11371         Compose *compose = (Compose *)data;
11372
11373         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11374 }
11375
11376 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
11377 {
11378         Compose *compose = (Compose *)data;
11379
11380         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11381 }
11382
11383 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
11384 {
11385         g_free(compose->privacy_system);
11386         g_free(compose->encdata);
11387
11388         compose->privacy_system = g_strdup(account->default_privacy_system);
11389         compose_update_privacy_system_menu_item(compose, warn);
11390 }
11391
11392 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
11393 {
11394         Compose *compose = (Compose *)data;
11395
11396         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
11397                 gtk_widget_show(compose->ruler_hbox);
11398                 prefs_common.show_ruler = TRUE;
11399         } else {
11400                 gtk_widget_hide(compose->ruler_hbox);
11401                 gtk_widget_queue_resize(compose->edit_vbox);
11402                 prefs_common.show_ruler = FALSE;
11403         }
11404 }
11405
11406 static void compose_attach_drag_received_cb (GtkWidget          *widget,
11407                                              GdkDragContext     *context,
11408                                              gint                x,
11409                                              gint                y,
11410                                              GtkSelectionData   *data,
11411                                              guint               info,
11412                                              guint               time,
11413                                              gpointer            user_data)
11414 {
11415         Compose *compose = (Compose *)user_data;
11416         GList *list, *tmp;
11417         GdkAtom type;
11418
11419         type = gtk_selection_data_get_data_type(data);
11420         if ((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
11421            && gtk_drag_get_source_widget(context) !=
11422                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11423                 list = uri_list_extract_filenames(
11424                         (const gchar *)gtk_selection_data_get_data(data));
11425                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11426                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
11427                         compose_attach_append
11428                                 (compose, (const gchar *)tmp->data,
11429                                  utf8_filename, NULL, NULL);
11430                         g_free(utf8_filename);
11431                 }
11432                 if (list) compose_changed_cb(NULL, compose);
11433                 list_free_strings(list);
11434                 g_list_free(list);
11435         } else if (gtk_drag_get_source_widget(context) 
11436                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11437                 /* comes from our summaryview */
11438                 SummaryView * summaryview = NULL;
11439                 GSList * list = NULL, *cur = NULL;
11440                 
11441                 if (mainwindow_get_mainwindow())
11442                         summaryview = mainwindow_get_mainwindow()->summaryview;
11443                 
11444                 if (summaryview)
11445                         list = summary_get_selected_msg_list(summaryview);
11446                 
11447                 for (cur = list; cur; cur = cur->next) {
11448                         MsgInfo *msginfo = (MsgInfo *)cur->data;
11449                         gchar *file = NULL;
11450                         if (msginfo)
11451                                 file = procmsg_get_message_file_full(msginfo, 
11452                                         TRUE, TRUE);
11453                         if (file) {
11454                                 compose_attach_append(compose, (const gchar *)file, 
11455                                         (const gchar *)file, "message/rfc822", NULL);
11456                                 g_free(file);
11457                         }
11458                 }
11459                 g_slist_free(list);
11460         }
11461 }
11462
11463 static gboolean compose_drag_drop(GtkWidget *widget,
11464                                   GdkDragContext *drag_context,
11465                                   gint x, gint y,
11466                                   guint time, gpointer user_data)
11467 {
11468         /* not handling this signal makes compose_insert_drag_received_cb
11469          * called twice */
11470         return TRUE;                                     
11471 }
11472
11473 static gboolean completion_set_focus_to_subject
11474                                         (GtkWidget    *widget,
11475                                          GdkEventKey  *event,
11476                                          Compose      *compose)
11477 {
11478         cm_return_val_if_fail(compose != NULL, FALSE);
11479
11480         /* make backtab move to subject field */
11481         if(event->keyval == GDK_KEY_ISO_Left_Tab) {
11482                 gtk_widget_grab_focus(compose->subject_entry);
11483                 return TRUE;
11484         }
11485         return FALSE;
11486 }
11487
11488 static void compose_insert_drag_received_cb (GtkWidget          *widget,
11489                                              GdkDragContext     *drag_context,
11490                                              gint                x,
11491                                              gint                y,
11492                                              GtkSelectionData   *data,
11493                                              guint               info,
11494                                              guint               time,
11495                                              gpointer            user_data)
11496 {
11497         Compose *compose = (Compose *)user_data;
11498         GList *list, *tmp;
11499         GdkAtom type;
11500         guint num_files;
11501         gchar *msg;
11502
11503         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11504          * does not work */
11505         type = gtk_selection_data_get_data_type(data);
11506         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
11507                 AlertValue val = G_ALERTDEFAULT;
11508                 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
11509
11510                 list = uri_list_extract_filenames(ddata);
11511                 num_files = g_list_length(list);
11512                 if (list == NULL && strstr(ddata, "://")) {
11513                         /* Assume a list of no files, and data has ://, is a remote link */
11514                         gchar *tmpdata = g_strstrip(g_strdup(ddata));
11515                         gchar *tmpfile = get_tmp_file();
11516                         str_write_to_file(tmpdata, tmpfile);
11517                         g_free(tmpdata);  
11518                         compose_insert_file(compose, tmpfile);
11519                         claws_unlink(tmpfile);
11520                         g_free(tmpfile);
11521                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11522                         compose_beautify_paragraph(compose, NULL, TRUE);
11523                         return;
11524                 }
11525                 switch (prefs_common.compose_dnd_mode) {
11526                         case COMPOSE_DND_ASK:
11527                                 msg = g_strdup_printf(
11528                                                 ngettext(
11529                                                         "Do you want to insert the contents of the file "
11530                                                         "into the message body, or attach it to the email?",
11531                                                         "Do you want to insert the contents of the %d files "
11532                                                         "into the message body, or attach them to the email?",
11533                                                         num_files),
11534                                                 num_files);
11535                                 val = alertpanel_full(_("Insert or attach?"), msg,
11536                                           GTK_STOCK_CANCEL, g_strconcat("+", _("_Insert"), NULL), _("_Attach"),
11537                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
11538                                 g_free(msg);
11539                                 break;
11540                         case COMPOSE_DND_INSERT:
11541                                 val = G_ALERTALTERNATE;
11542                                 break;
11543                         case COMPOSE_DND_ATTACH:
11544                                 val = G_ALERTOTHER;
11545                                 break;
11546                         default:
11547                                 /* unexpected case */
11548                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11549                 }
11550
11551                 if (val & G_ALERTDISABLE) {
11552                         val &= ~G_ALERTDISABLE;
11553                         /* remember what action to perform by default, only if we don't click Cancel */
11554                         if (val == G_ALERTALTERNATE)
11555                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
11556                         else if (val == G_ALERTOTHER)
11557                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
11558                 }
11559
11560                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
11561                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
11562                         list_free_strings(list);
11563                         g_list_free(list);
11564                         return;
11565                 } else if (val == G_ALERTOTHER) {
11566                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
11567                         list_free_strings(list);
11568                         g_list_free(list);
11569                         return;
11570                 } 
11571
11572                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11573                         compose_insert_file(compose, (const gchar *)tmp->data);
11574                 }
11575                 list_free_strings(list);
11576                 g_list_free(list);
11577                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11578                 return;
11579         }
11580 }
11581
11582 static void compose_header_drag_received_cb (GtkWidget          *widget,
11583                                              GdkDragContext     *drag_context,
11584                                              gint                x,
11585                                              gint                y,
11586                                              GtkSelectionData   *data,
11587                                              guint               info,
11588                                              guint               time,
11589                                              gpointer            user_data)
11590 {
11591         GtkEditable *entry = (GtkEditable *)user_data;
11592         const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
11593
11594         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11595          * does not work */
11596
11597         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
11598                 gchar *decoded=g_new(gchar, strlen(email));
11599                 int start = 0;
11600
11601                 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
11602                 gtk_editable_delete_text(entry, 0, -1);
11603                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11604                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11605                 g_free(decoded);
11606                 return;
11607         }
11608         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11609 }
11610
11611 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11612 {
11613         Compose *compose = (Compose *)data;
11614
11615         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11616                 compose->return_receipt = TRUE;
11617         else
11618                 compose->return_receipt = FALSE;
11619 }
11620
11621 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11622 {
11623         Compose *compose = (Compose *)data;
11624
11625         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11626                 compose->remove_references = TRUE;
11627         else
11628                 compose->remove_references = FALSE;
11629 }
11630
11631 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11632                                         ComposeHeaderEntry *headerentry)
11633 {
11634         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11635         return FALSE;
11636 }
11637
11638 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11639                                             GdkEventKey *event,
11640                                             ComposeHeaderEntry *headerentry)
11641 {
11642         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11643             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11644             !(event->state & GDK_MODIFIER_MASK) &&
11645             (event->keyval == GDK_KEY_BackSpace) &&
11646             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11647                 gtk_container_remove
11648                         (GTK_CONTAINER(headerentry->compose->header_table),
11649                          headerentry->combo);
11650                 gtk_container_remove
11651                         (GTK_CONTAINER(headerentry->compose->header_table),
11652                          headerentry->entry);
11653                 headerentry->compose->header_list =
11654                         g_slist_remove(headerentry->compose->header_list,
11655                                        headerentry);
11656                 g_free(headerentry);
11657         } else  if (event->keyval == GDK_KEY_Tab) {
11658                 if (headerentry->compose->header_last == headerentry) {
11659                         /* Override default next focus, and give it to subject_entry
11660                          * instead of notebook tabs
11661                          */
11662                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
11663                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
11664                         return TRUE;
11665                 }
11666         }
11667         return FALSE;
11668 }
11669
11670 static gboolean scroll_postpone(gpointer data)
11671 {
11672         Compose *compose = (Compose *)data;
11673
11674         if (compose->batch)
11675                 return FALSE;
11676
11677         GTK_EVENTS_FLUSH();
11678         compose_show_first_last_header(compose, FALSE);
11679         return FALSE;
11680 }
11681
11682 static void compose_headerentry_changed_cb(GtkWidget *entry,
11683                                     ComposeHeaderEntry *headerentry)
11684 {
11685         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11686                 compose_create_header_entry(headerentry->compose);
11687                 g_signal_handlers_disconnect_matched
11688                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11689                          0, 0, NULL, NULL, headerentry);
11690
11691                 if (!headerentry->compose->batch)
11692                         g_timeout_add(0, scroll_postpone, headerentry->compose);
11693         }
11694 }
11695
11696 static gboolean compose_defer_auto_save_draft(Compose *compose)
11697 {
11698         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
11699         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11700         return FALSE;
11701 }
11702
11703 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11704 {
11705         GtkAdjustment *vadj;
11706
11707         cm_return_if_fail(compose);
11708
11709         if(compose->batch)
11710                 return;
11711
11712         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11713         cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11714         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11715                                 gtk_widget_get_parent(compose->header_table)));
11716         gtk_adjustment_set_value(vadj, (show_first ?
11717                                 gtk_adjustment_get_lower(vadj) :
11718                                 (gtk_adjustment_get_upper(vadj) -
11719                                 gtk_adjustment_get_page_size(vadj))));
11720         gtk_adjustment_changed(vadj);
11721 }
11722
11723 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11724                           const gchar *text, gint len, Compose *compose)
11725 {
11726         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11727                                 (G_OBJECT(compose->text), "paste_as_quotation"));
11728         GtkTextMark *mark;
11729
11730         cm_return_if_fail(text != NULL);
11731
11732         g_signal_handlers_block_by_func(G_OBJECT(buffer),
11733                                         G_CALLBACK(text_inserted),
11734                                         compose);
11735         if (paste_as_quotation) {
11736                 gchar *new_text;
11737                 const gchar *qmark;
11738                 guint pos = 0;
11739                 GtkTextIter start_iter;
11740
11741                 if (len < 0)
11742                         len = strlen(text);
11743
11744                 new_text = g_strndup(text, len);
11745
11746                 qmark = compose_quote_char_from_context(compose);
11747
11748                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11749                 gtk_text_buffer_place_cursor(buffer, iter);
11750
11751                 pos = gtk_text_iter_get_offset(iter);
11752
11753                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11754                                                   _("Quote format error at line %d."));
11755                 quote_fmt_reset_vartable();
11756                 g_free(new_text);
11757                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11758                                   GINT_TO_POINTER(paste_as_quotation - 1));
11759                                   
11760                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11761                 gtk_text_buffer_place_cursor(buffer, iter);
11762                 gtk_text_buffer_delete_mark(buffer, mark);
11763
11764                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11765                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11766                 compose_beautify_paragraph(compose, &start_iter, FALSE);
11767                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11768                 gtk_text_buffer_delete_mark(buffer, mark);
11769         } else {
11770                 if (strcmp(text, "\n") || compose->automatic_break
11771                 || gtk_text_iter_starts_line(iter)) {
11772                         GtkTextIter before_ins;
11773                         gtk_text_buffer_insert(buffer, iter, text, len);
11774                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11775                                 before_ins = *iter; 
11776                                 gtk_text_iter_backward_chars(&before_ins, len);
11777                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11778                         }
11779                 } else {
11780                         /* check if the preceding is just whitespace or quote */
11781                         GtkTextIter start_line;
11782                         gchar *tmp = NULL, *quote = NULL;
11783                         gint quote_len = 0, is_normal = 0;
11784                         start_line = *iter;
11785                         gtk_text_iter_set_line_offset(&start_line, 0); 
11786                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11787                         g_strstrip(tmp);
11788
11789                         if (*tmp == '\0') {
11790                                 is_normal = 1;
11791                         } else {
11792                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
11793                                 if (quote)
11794                                         is_normal = 1;
11795                                 g_free(quote);
11796                         }
11797                         g_free(tmp);
11798                         
11799                         if (is_normal) {
11800                                 gtk_text_buffer_insert(buffer, iter, text, len);
11801                         } else {
11802                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
11803                                         iter, text, len, "no_join", NULL);
11804                         }
11805                 }
11806         }
11807         
11808         if (!paste_as_quotation) {
11809                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11810                 compose_beautify_paragraph(compose, iter, FALSE);
11811                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11812                 gtk_text_buffer_delete_mark(buffer, mark);
11813         }
11814
11815         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11816                                           G_CALLBACK(text_inserted),
11817                                           compose);
11818         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11819
11820         if (compose_can_autosave(compose) && 
11821             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11822             compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN /* disabled while loading */)
11823                 compose->draft_timeout_tag = g_timeout_add
11824                         (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11825 }
11826
11827 #if USE_ENCHANT
11828 static void compose_check_all(GtkAction *action, gpointer data)
11829 {
11830         Compose *compose = (Compose *)data;
11831         if (!compose->gtkaspell)
11832                 return;
11833                 
11834         if (gtk_widget_has_focus(compose->subject_entry))
11835                 claws_spell_entry_check_all(
11836                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
11837         else
11838                 gtkaspell_check_all(compose->gtkaspell);
11839 }
11840
11841 static void compose_highlight_all(GtkAction *action, gpointer data)
11842 {
11843         Compose *compose = (Compose *)data;
11844         if (compose->gtkaspell) {
11845                 claws_spell_entry_recheck_all(
11846                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11847                 gtkaspell_highlight_all(compose->gtkaspell);
11848         }
11849 }
11850
11851 static void compose_check_backwards(GtkAction *action, gpointer data)
11852 {
11853         Compose *compose = (Compose *)data;
11854         if (!compose->gtkaspell) {
11855                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11856                 return;
11857         }
11858
11859         if (gtk_widget_has_focus(compose->subject_entry))
11860                 claws_spell_entry_check_backwards(
11861                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11862         else
11863                 gtkaspell_check_backwards(compose->gtkaspell);
11864 }
11865
11866 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11867 {
11868         Compose *compose = (Compose *)data;
11869         if (!compose->gtkaspell) {
11870                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11871                 return;
11872         }
11873
11874         if (gtk_widget_has_focus(compose->subject_entry))
11875                 claws_spell_entry_check_forwards_go(
11876                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11877         else
11878                 gtkaspell_check_forwards_go(compose->gtkaspell);
11879 }
11880 #endif
11881
11882 /*!
11883  *\brief        Guess originating forward account from MsgInfo and several 
11884  *              "common preference" settings. Return NULL if no guess. 
11885  */
11886 static PrefsAccount *compose_find_account(MsgInfo *msginfo)
11887 {
11888         PrefsAccount *account = NULL;
11889         
11890         cm_return_val_if_fail(msginfo, NULL);
11891         cm_return_val_if_fail(msginfo->folder, NULL);
11892         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11893
11894         if (msginfo->folder->prefs->enable_default_account)
11895                 account = account_find_from_id(msginfo->folder->prefs->default_account);
11896                 
11897         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11898                 gchar *to;
11899                 Xstrdup_a(to, msginfo->to, return NULL);
11900                 extract_address(to);
11901                 account = account_find_from_address(to, FALSE);
11902         }
11903
11904         if (!account && prefs_common.forward_account_autosel) {
11905                 gchar *cc = NULL;
11906                 if (!procheader_get_header_from_msginfo
11907                                 (msginfo, &cc, "Cc:")) { 
11908                         gchar *buf = cc + strlen("Cc:");
11909                         extract_address(buf);
11910                         account = account_find_from_address(buf, FALSE);
11911                         g_free(cc);
11912                 }
11913         }
11914         
11915         if (!account && prefs_common.forward_account_autosel) {
11916                 gchar *deliveredto = NULL;
11917                 if (!procheader_get_header_from_msginfo
11918                                 (msginfo, &deliveredto, "Delivered-To:")) { 
11919                         gchar *buf = deliveredto + strlen("Delivered-To:");
11920                         extract_address(buf);
11921                         account = account_find_from_address(buf, FALSE);
11922                         g_free(deliveredto);
11923                 }
11924         }
11925
11926         if (!account)
11927                 account = msginfo->folder->folder->account;
11928         
11929         return account;
11930 }
11931
11932 gboolean compose_close(Compose *compose)
11933 {
11934         gint x, y;
11935
11936         cm_return_val_if_fail(compose, FALSE);
11937
11938         if (!g_mutex_trylock(compose->mutex)) {
11939                 /* we have to wait for the (possibly deferred by auto-save)
11940                  * drafting to be done, before destroying the compose under
11941                  * it. */
11942                 debug_print("waiting for drafting to finish...\n");
11943                 compose_allow_user_actions(compose, FALSE);
11944                 if (compose->close_timeout_tag == 0) {
11945                         compose->close_timeout_tag = 
11946                                 g_timeout_add (500, (GSourceFunc) compose_close,
11947                                 compose);
11948                 }
11949                 return TRUE;
11950         }
11951
11952         if (compose->draft_timeout_tag >= 0) {
11953                 g_source_remove(compose->draft_timeout_tag);
11954                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;
11955         }
11956
11957         gtkut_widget_get_uposition(compose->window, &x, &y);
11958         if (!compose->batch) {
11959                 prefs_common.compose_x = x;
11960                 prefs_common.compose_y = y;
11961         }
11962         g_mutex_unlock(compose->mutex);
11963         compose_destroy(compose);
11964         return FALSE;
11965 }
11966
11967 /**
11968  * Add entry field for each address in list.
11969  * \param compose     E-Mail composition object.
11970  * \param listAddress List of (formatted) E-Mail addresses.
11971  */
11972 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11973         GList *node;
11974         gchar *addr;
11975         node = listAddress;
11976         while( node ) {
11977                 addr = ( gchar * ) node->data;
11978                 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11979                 node = g_list_next( node );
11980         }
11981 }
11982
11983 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
11984                                     guint action, gboolean opening_multiple)
11985 {
11986         gchar *body = NULL;
11987         GSList *new_msglist = NULL;
11988         MsgInfo *tmp_msginfo = NULL;
11989         gboolean originally_enc = FALSE;
11990         gboolean originally_sig = FALSE;
11991         Compose *compose = NULL;
11992         gchar *s_system = NULL;
11993
11994         cm_return_if_fail(msgview != NULL);
11995
11996         cm_return_if_fail(msginfo_list != NULL);
11997
11998         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11999                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
12000                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
12001
12002                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
12003                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
12004                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
12005                                                 orig_msginfo, mimeinfo);
12006                         if (tmp_msginfo != NULL) {
12007                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
12008
12009                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
12010                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
12011                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
12012
12013                                 tmp_msginfo->folder = orig_msginfo->folder;
12014                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
12015                                 if (orig_msginfo->tags) {
12016                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
12017                                         tmp_msginfo->folder->tags_dirty = TRUE;
12018                                 }
12019                         }
12020                 }
12021         }
12022
12023         if (!opening_multiple)
12024                 body = messageview_get_selection(msgview);
12025
12026         if (new_msglist) {
12027                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
12028                 procmsg_msginfo_free(&tmp_msginfo);
12029                 g_slist_free(new_msglist);
12030         } else
12031                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
12032
12033         if (compose && originally_enc) {
12034                 compose_force_encryption(compose, compose->account, FALSE, s_system);
12035         }
12036
12037         if (compose && originally_sig && compose->account->default_sign_reply) {
12038                 compose_force_signing(compose, compose->account, s_system);
12039         }
12040         g_free(s_system);
12041         g_free(body);
12042         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
12043 }
12044
12045 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
12046                                     guint action)
12047 {
12048         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
12049         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
12050                 GSList *cur = msginfo_list;
12051                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
12052                                                "messages. Opening the windows "
12053                                                "could take some time. Do you "
12054                                                "want to continue?"), 
12055                                                g_slist_length(msginfo_list));
12056                 if (g_slist_length(msginfo_list) > 9
12057                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
12058                     != G_ALERTALTERNATE) {
12059                         g_free(msg);
12060                         return;
12061                 }
12062                 g_free(msg);
12063                 /* We'll open multiple compose windows */
12064                 /* let the WM place the next windows */
12065                 compose_force_window_origin = FALSE;
12066                 for (; cur; cur = cur->next) {
12067                         GSList tmplist;
12068                         tmplist.data = cur->data;
12069                         tmplist.next = NULL;
12070                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
12071                 }
12072                 compose_force_window_origin = TRUE;
12073         } else {
12074                 /* forwarding multiple mails as attachments is done via a
12075                  * single compose window */
12076                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
12077         }
12078 }
12079
12080 void compose_check_for_email_account(Compose *compose)
12081 {
12082         PrefsAccount *ac = NULL, *curr = NULL;
12083         GList *list;
12084         
12085         if (!compose)
12086                 return;
12087
12088         if (compose->account && compose->account->protocol == A_NNTP) {
12089                 ac = account_get_cur_account();
12090                 if (ac->protocol == A_NNTP) {
12091                         list = account_get_list();
12092                         
12093                         for( ; list != NULL ; list = g_list_next(list)) {
12094                                 curr = (PrefsAccount *) list->data;
12095                                 if (curr->protocol != A_NNTP) {
12096                                         ac = curr;
12097                                         break;
12098                                 }
12099                         }
12100                 }
12101                 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
12102                                         ac->account_id); 
12103         }
12104 }
12105
12106 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo, 
12107                                 const gchar *address)
12108 {
12109         GSList *msginfo_list = NULL;
12110         gchar *body =  messageview_get_selection(msgview);
12111         Compose *compose;
12112         
12113         msginfo_list = g_slist_prepend(msginfo_list, msginfo);
12114         
12115         compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
12116         compose_check_for_email_account(compose);
12117         compose_set_folder_prefs(compose, msginfo->folder, FALSE);
12118         compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
12119         compose_reply_set_subject(compose, msginfo);
12120
12121         g_free(body);
12122         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
12123 }
12124
12125 void compose_set_position(Compose *compose, gint pos)
12126 {
12127         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
12128
12129         gtkut_text_view_set_position(text, pos);
12130 }
12131
12132 gboolean compose_search_string(Compose *compose,
12133                                 const gchar *str, gboolean case_sens)
12134 {
12135         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
12136
12137         return gtkut_text_view_search_string(text, str, case_sens);
12138 }
12139
12140 gboolean compose_search_string_backward(Compose *compose,
12141                                 const gchar *str, gboolean case_sens)
12142 {
12143         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
12144
12145         return gtkut_text_view_search_string_backward(text, str, case_sens);
12146 }
12147
12148 /* allocate a msginfo structure and populate its data from a compose data structure */
12149 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
12150 {
12151         MsgInfo *newmsginfo;
12152         GSList *list;
12153         gchar date[RFC822_DATE_BUFFSIZE];
12154
12155         cm_return_val_if_fail( compose != NULL, NULL );
12156
12157         newmsginfo = procmsg_msginfo_new();
12158
12159         /* date is now */
12160         get_rfc822_date(date, sizeof(date));
12161         newmsginfo->date = g_strdup(date);
12162
12163         /* from */
12164         if (compose->from_name) {
12165                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
12166                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
12167         }
12168
12169         /* subject */
12170         if (compose->subject_entry)
12171                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
12172
12173         /* to, cc, reply-to, newsgroups */
12174         for (list = compose->header_list; list; list = list->next) {
12175                 gchar *header = gtk_editable_get_chars(
12176                                                                 GTK_EDITABLE(
12177                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
12178                 gchar *entry = gtk_editable_get_chars(
12179                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
12180
12181                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
12182                         if ( newmsginfo->to == NULL ) {
12183                                 newmsginfo->to = g_strdup(entry);
12184                         } else if (entry && *entry) {
12185                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
12186                                 g_free(newmsginfo->to);
12187                                 newmsginfo->to = tmp;
12188                         }
12189                 } else
12190                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
12191                         if ( newmsginfo->cc == NULL ) {
12192                                 newmsginfo->cc = g_strdup(entry);
12193                         } else if (entry && *entry) {
12194                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
12195                                 g_free(newmsginfo->cc);
12196                                 newmsginfo->cc = tmp;
12197                         }
12198                 } else
12199                 if ( strcasecmp(header,
12200                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
12201                         if ( newmsginfo->newsgroups == NULL ) {
12202                                 newmsginfo->newsgroups = g_strdup(entry);
12203                         } else if (entry && *entry) {
12204                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
12205                                 g_free(newmsginfo->newsgroups);
12206                                 newmsginfo->newsgroups = tmp;
12207                         }
12208                 }
12209
12210                 g_free(header);
12211                 g_free(entry);  
12212         }
12213
12214         /* other data is unset */
12215
12216         return newmsginfo;
12217 }
12218
12219 #ifdef USE_ENCHANT
12220 /* update compose's dictionaries from folder dict settings */
12221 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
12222                                                 FolderItem *folder_item)
12223 {
12224         cm_return_if_fail(compose != NULL);
12225
12226         if (compose->gtkaspell && folder_item && folder_item->prefs) {
12227                 FolderItemPrefs *prefs = folder_item->prefs;
12228
12229                 if (prefs->enable_default_dictionary)
12230                         gtkaspell_change_dict(compose->gtkaspell,
12231                                         prefs->default_dictionary, FALSE);
12232                 if (folder_item->prefs->enable_default_alt_dictionary)
12233                         gtkaspell_change_alt_dict(compose->gtkaspell,
12234                                         prefs->default_alt_dictionary);
12235                 if (prefs->enable_default_dictionary
12236                         || prefs->enable_default_alt_dictionary)
12237                         compose_spell_menu_changed(compose);
12238         }
12239 }
12240 #endif
12241
12242 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
12243 {
12244         Compose *compose = (Compose *)data;
12245
12246         cm_return_if_fail(compose != NULL);
12247
12248         gtk_widget_grab_focus(compose->text);
12249 }
12250
12251 static void from_name_activate_cb(GtkWidget *widget, gpointer data)
12252 {
12253         gtk_combo_box_popup(GTK_COMBO_BOX(data));
12254 }
12255
12256
12257 /*
12258  * End of Source.
12259  */