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