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