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