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