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