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