Prefer dynamically allocated buffers to fixed-size ones, use the same
[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       perform_checks,
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 (!compose->batch && prefs_common.warn_sending_many_recipients == TRUE
5136                         && check_everything == TRUE) {
5137                 GSList *list;
5138                 gint cnt = 0;
5139
5140                 /* count To and Cc recipients */
5141                 for (list = compose->header_list; list; list = list->next) {
5142                         gchar *header;
5143                         gchar *entry;
5144
5145                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
5146                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
5147                         g_strstrip(header);
5148                         g_strstrip(entry);
5149                         if ((entry[0] != '\0')
5150                         &&      (strcmp(header, prefs_common_translated_header_name("To:"))
5151                         ||  strcmp(header, prefs_common_translated_header_name("Cc:")))) {
5152                                 cnt++;
5153                         }
5154                         g_free(header);
5155                         g_free(entry);
5156                 }
5157                 if (cnt > prefs_common.warn_sending_many_recipients_num) {
5158                         AlertValue aval;
5159                         gchar *button_label;
5160                         gchar *message;
5161
5162                         if (compose->sending)
5163                                 button_label = g_strconcat("+", _("_Send"), NULL);
5164                         else
5165                                 button_label = g_strconcat("+", _("_Queue"), NULL);
5166                         message = g_strdup_printf(_("Sending to %d recipients. %s"), cnt,
5167                                         compose->sending?_("Send it anyway?"):
5168                                         _("Queue it anyway?"));
5169
5170                         aval = alertpanel_full(compose->sending?_("Send"):_("Send later"), message,
5171                                                GTK_STOCK_CANCEL, button_label, NULL, TRUE, NULL,
5172                                                ALERT_QUESTION, G_ALERTDEFAULT);
5173                         g_free(message);
5174                         if (aval & G_ALERTDISABLE) {
5175                                 aval &= ~G_ALERTDISABLE;
5176                                 prefs_common.warn_empty_subj = FALSE;
5177                         }
5178                         if (aval != G_ALERTALTERNATE)
5179                                 return FALSE;
5180                 }
5181         }
5182
5183         if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
5184                 return FALSE;
5185
5186         return TRUE;
5187 }
5188
5189 gint compose_send(Compose *compose)
5190 {
5191         gint msgnum;
5192         FolderItem *folder = NULL;
5193         gint val = -1;
5194         gchar *msgpath = NULL;
5195         gboolean discard_window = FALSE;
5196         gchar *errstr = NULL;
5197         gchar *tmsgid = NULL;
5198         MainWindow *mainwin = mainwindow_get_mainwindow();
5199         gboolean queued_removed = FALSE;
5200
5201         if (prefs_common.send_dialog_invisible
5202                         || compose->batch == TRUE)
5203                 discard_window = TRUE;
5204
5205         compose_allow_user_actions (compose, FALSE);
5206         compose->sending = TRUE;
5207
5208         if (compose_check_entries(compose, TRUE) == FALSE) {
5209                 if (compose->batch) {
5210                         gtk_widget_show_all(compose->window);
5211                 }
5212                 goto bail;
5213         }
5214
5215         inc_lock();
5216         val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
5217
5218         if (val) {
5219                 if (compose->batch) {
5220                         gtk_widget_show_all(compose->window);
5221                 }
5222                 if (val == -4) {
5223                         alertpanel_error(_("Could not queue message for sending:\n\n"
5224                                            "Charset conversion failed."));
5225                 } else if (val == -5) {
5226                         alertpanel_error(_("Could not queue message for sending:\n\n"
5227                                            "Couldn't get recipient encryption key."));
5228                 } else if (val == -6) {
5229                         /* silent error */
5230                 } else if (val == -3) {
5231                         if (privacy_peek_error())
5232                         alertpanel_error(_("Could not queue message for sending:\n\n"
5233                                            "Signature failed: %s"), privacy_get_error());
5234                 } else if (val == -2 && errno != 0) {
5235                         alertpanel_error(_("Could not queue message for sending:\n\n%s."), g_strerror(errno));
5236                 } else {
5237                         alertpanel_error(_("Could not queue message for sending."));
5238                 }
5239                 goto bail;
5240         }
5241
5242         tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
5243         if (discard_window) {
5244                 compose->sending = FALSE;
5245                 compose_close(compose);
5246                 /* No more compose access in the normal codepath 
5247                  * after this point! */
5248                 compose = NULL;
5249         }
5250
5251         if (msgnum == 0) {
5252                 alertpanel_error(_("The message was queued but could not be "
5253                                    "sent.\nUse \"Send queued messages\" from "
5254                                    "the main window to retry."));
5255                 if (!discard_window) {
5256                         goto bail;
5257                 }
5258                 inc_unlock();
5259                 g_free(tmsgid);
5260                 return -1;
5261         }
5262         if (msgpath == NULL) {
5263                 msgpath = folder_item_fetch_msg(folder, msgnum);
5264                 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5265                 g_free(msgpath);
5266         } else {
5267                 val = procmsg_send_message_queue_with_lock(msgpath, &errstr, folder, msgnum, &queued_removed);
5268                 claws_unlink(msgpath);
5269                 g_free(msgpath);
5270         }
5271         if (!discard_window) {
5272                 if (val != 0) {
5273                         if (!queued_removed)
5274                                 folder_item_remove_msg(folder, msgnum);
5275                         folder_item_scan(folder);
5276                         if (tmsgid) {
5277                                 /* make sure we delete that */
5278                                 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5279                                 if (tmp) {
5280                                         debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5281                                         folder_item_remove_msg(folder, tmp->msgnum);
5282                                         procmsg_msginfo_free(&tmp);
5283                                 } 
5284                         }
5285                 }
5286         }
5287
5288         if (val == 0) {
5289                 if (!queued_removed)
5290                         folder_item_remove_msg(folder, msgnum);
5291                 folder_item_scan(folder);
5292                 if (tmsgid) {
5293                         /* make sure we delete that */
5294                         MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
5295                         if (tmp) {
5296                                 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
5297                                 folder_item_remove_msg(folder, tmp->msgnum);
5298                                 procmsg_msginfo_free(&tmp);
5299                         }
5300                 }
5301                 if (!discard_window) {
5302                         compose->sending = FALSE;
5303                         compose_allow_user_actions (compose, TRUE);
5304                         compose_close(compose);
5305                 }
5306         } else {
5307                 if (errstr) {
5308                         alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
5309                                    "the main window to retry."), errstr);
5310                         g_free(errstr);
5311                 } else {
5312                         alertpanel_error_log(_("The message was queued but could not be "
5313                                    "sent.\nUse \"Send queued messages\" from "
5314                                    "the main window to retry."));
5315                 }
5316                 if (!discard_window) {
5317                         goto bail;              
5318                 }
5319                 inc_unlock();
5320                 g_free(tmsgid);
5321                 return -1;
5322         }
5323         g_free(tmsgid);
5324         inc_unlock();
5325         toolbar_main_set_sensitive(mainwin);
5326         main_window_set_menu_sensitive(mainwin);
5327         return 0;
5328
5329 bail:
5330         inc_unlock();
5331         g_free(tmsgid);
5332         compose_allow_user_actions (compose, TRUE);
5333         compose->sending = FALSE;
5334         compose->modified = TRUE; 
5335         toolbar_main_set_sensitive(mainwin);
5336         main_window_set_menu_sensitive(mainwin);
5337
5338         return -1;
5339 }
5340
5341 static gboolean compose_use_attach(Compose *compose) 
5342 {
5343         GtkTreeModel *model = gtk_tree_view_get_model
5344                                 (GTK_TREE_VIEW(compose->attach_clist));
5345         return gtk_tree_model_iter_n_children(model, NULL) > 0;
5346 }
5347
5348 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
5349                                                            FILE *fp)
5350 {
5351         gchar buf[BUFFSIZE];
5352         gchar *str;
5353         gboolean first_to_address;
5354         gboolean first_cc_address;
5355         GSList *list;
5356         ComposeHeaderEntry *headerentry;
5357         const gchar *headerentryname;
5358         const gchar *cc_hdr;
5359         const gchar *to_hdr;
5360         gboolean err = FALSE;
5361
5362         debug_print("Writing redirect header\n");
5363
5364         cc_hdr = prefs_common_translated_header_name("Cc:");
5365         to_hdr = prefs_common_translated_header_name("To:");
5366
5367         first_to_address = TRUE;
5368         for (list = compose->header_list; list; list = list->next) {
5369                 headerentry = ((ComposeHeaderEntry *)list->data);
5370                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5371
5372                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
5373                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5374                         Xstrdup_a(str, entstr, return -1);
5375                         g_strstrip(str);
5376                         if (str[0] != '\0') {
5377                                 compose_convert_header
5378                                         (compose, buf, sizeof(buf), str,
5379                                         strlen("Resent-To") + 2, TRUE);
5380
5381                                 if (first_to_address) {
5382                                         err |= (fprintf(fp, "Resent-To: ") < 0);
5383                                         first_to_address = FALSE;
5384                                 } else {
5385                                         err |= (fprintf(fp, ",") < 0);
5386                                 }
5387                                 err |= (fprintf(fp, "%s", buf) < 0);
5388                         }
5389                 }
5390         }
5391         if (!first_to_address) {
5392                 err |= (fprintf(fp, "\n") < 0);
5393         }
5394
5395         first_cc_address = TRUE;
5396         for (list = compose->header_list; list; list = list->next) {
5397                 headerentry = ((ComposeHeaderEntry *)list->data);
5398                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5399
5400                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
5401                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5402                         Xstrdup_a(str, strg, return -1);
5403                         g_strstrip(str);
5404                         if (str[0] != '\0') {
5405                                 compose_convert_header
5406                                         (compose, buf, sizeof(buf), str,
5407                                         strlen("Resent-Cc") + 2, TRUE);
5408
5409                                 if (first_cc_address) {
5410                                         err |= (fprintf(fp, "Resent-Cc: ") < 0);
5411                                         first_cc_address = FALSE;
5412                                 } else {
5413                                         err |= (fprintf(fp, ",") < 0);
5414                                 }
5415                                 err |= (fprintf(fp, "%s", buf) < 0);
5416                         }
5417                 }
5418         }
5419         if (!first_cc_address) {
5420                 err |= (fprintf(fp, "\n") < 0);
5421         }
5422         
5423         return (err ? -1:0);
5424 }
5425
5426 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5427 {
5428         gchar buf[BUFFSIZE];
5429         gchar *str;
5430         const gchar *entstr;
5431         /* struct utsname utsbuf; */
5432         gboolean err = FALSE;
5433
5434         cm_return_val_if_fail(fp != NULL, -1);
5435         cm_return_val_if_fail(compose->account != NULL, -1);
5436         cm_return_val_if_fail(compose->account->address != NULL, -1);
5437
5438         /* Resent-Date */
5439         if (prefs_common.hide_timezone)
5440                 get_rfc822_date_hide_tz(buf, sizeof(buf));
5441         else
5442                 get_rfc822_date(buf, sizeof(buf));
5443         err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
5444
5445         /* Resent-From */
5446         if (compose->account->name && *compose->account->name) {
5447                 compose_convert_header
5448                         (compose, buf, sizeof(buf), compose->account->name,
5449                          strlen("From: "), TRUE);
5450                 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
5451                         buf, compose->account->address) < 0);
5452         } else
5453                 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
5454
5455         /* Subject */
5456         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5457         if (*entstr != '\0') {
5458                 Xstrdup_a(str, entstr, return -1);
5459                 g_strstrip(str);
5460                 if (*str != '\0') {
5461                         compose_convert_header(compose, buf, sizeof(buf), str,
5462                                                strlen("Subject: "), FALSE);
5463                         err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
5464                 }
5465         }
5466
5467         /* Resent-Message-ID */
5468         if (compose->account->gen_msgid) {
5469                 gchar *addr = prefs_account_generate_msgid(compose->account);
5470                 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", addr) < 0);
5471                 if (compose->msgid)
5472                         g_free(compose->msgid);
5473                 compose->msgid = addr;
5474         } else {
5475                 compose->msgid = NULL;
5476         }
5477
5478         if (compose_redirect_write_headers_from_headerlist(compose, fp))
5479                 return -1;
5480
5481         /* separator between header and body */
5482         err |= (fputs("\n", fp) == EOF);
5483
5484         return (err ? -1:0);
5485 }
5486
5487 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5488 {
5489         FILE *fp;
5490         size_t len;
5491         gchar buf[BUFFSIZE];
5492         int i = 0;
5493         gboolean skip = FALSE;
5494         gboolean err = FALSE;
5495         gchar *not_included[]={
5496                 "Return-Path:",         "Delivered-To:",        "Received:",
5497                 "Subject:",             "X-UIDL:",              "AF:",
5498                 "NF:",                  "PS:",                  "SRH:",
5499                 "SFN:",                 "DSR:",                 "MID:",
5500                 "CFG:",                 "PT:",                  "S:",
5501                 "RQ:",                  "SSV:",                 "NSV:",
5502                 "SSH:",                 "R:",                   "MAID:",
5503                 "NAID:",                "RMID:",                "FMID:",
5504                 "SCF:",                 "RRCPT:",               "NG:",
5505                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
5506                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
5507                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
5508                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
5509                 "X-Claws-Auto-Wrapping:", "X-Claws-Auto-Indent:",
5510                 NULL
5511                 };
5512         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5513                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5514                 return -1;
5515         }
5516
5517         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5518                 skip = FALSE;
5519                 for (i = 0; not_included[i] != NULL; i++) {
5520                         if (g_ascii_strncasecmp(buf, not_included[i],
5521                                                 strlen(not_included[i])) == 0) {
5522                                 skip = TRUE;
5523                                 break;
5524                         }
5525                 }
5526                 if (skip)
5527                         continue;
5528                 if (fputs(buf, fdest) == -1)
5529                         goto error;
5530
5531                 if (!prefs_common.redirect_keep_from) {
5532                         if (g_ascii_strncasecmp(buf, "From:",
5533                                           strlen("From:")) == 0) {
5534                                 err |= (fputs(" (by way of ", fdest) == EOF);
5535                                 if (compose->account->name
5536                                     && *compose->account->name) {
5537                                         compose_convert_header
5538                                                 (compose, buf, sizeof(buf),
5539                                                  compose->account->name,
5540                                                  strlen("From: "),
5541                                                  FALSE);
5542                                         err |= (fprintf(fdest, "%s <%s>",
5543                                                 buf,
5544                                                 compose->account->address) < 0);
5545                                 } else
5546                                         err |= (fprintf(fdest, "%s",
5547                                                 compose->account->address) < 0);
5548                                 err |= (fputs(")", fdest) == EOF);
5549                         }
5550                 }
5551
5552                 if (fputs("\n", fdest) == -1)
5553                         goto error;
5554         }
5555
5556         if (err)
5557                 goto error;
5558
5559         if (compose_redirect_write_headers(compose, fdest))
5560                 goto error;
5561
5562         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5563                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5564                         goto error;
5565         }
5566
5567         fclose(fp);
5568
5569         return 0;
5570 error:
5571         fclose(fp);
5572
5573         return -1;
5574 }
5575
5576 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5577 {
5578         GtkTextBuffer *buffer;
5579         GtkTextIter start, end;
5580         gchar *chars, *tmp_enc_file, *content;
5581         gchar *buf, *msg;
5582         const gchar *out_codeset;
5583         EncodingType encoding = ENC_UNKNOWN;
5584         MimeInfo *mimemsg, *mimetext;
5585         gint line;
5586         const gchar *src_codeset = CS_INTERNAL;
5587         gchar *from_addr = NULL;
5588         gchar *from_name = NULL;
5589         FolderItem *outbox;
5590
5591         if (action == COMPOSE_WRITE_FOR_SEND) {
5592                 attach_parts = TRUE;
5593
5594                 /* We're sending the message, generate a Message-ID
5595                  * if necessary. */
5596                 if (compose->msgid == NULL &&
5597                                 compose->account->gen_msgid) {
5598                         compose->msgid = prefs_account_generate_msgid(compose->account);
5599                 }
5600         }
5601
5602         /* create message MimeInfo */
5603         mimemsg = procmime_mimeinfo_new();
5604         mimemsg->type = MIMETYPE_MESSAGE;
5605         mimemsg->subtype = g_strdup("rfc822");
5606         mimemsg->content = MIMECONTENT_MEM;
5607         mimemsg->tmp = TRUE; /* must free content later */
5608         mimemsg->data.mem = compose_get_header(compose);
5609
5610         /* Create text part MimeInfo */
5611         /* get all composed text */
5612         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5613         gtk_text_buffer_get_start_iter(buffer, &start);
5614         gtk_text_buffer_get_end_iter(buffer, &end);
5615         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5616
5617         out_codeset = conv_get_charset_str(compose->out_encoding);
5618
5619         if (!out_codeset && is_ascii_str(chars)) {
5620                 out_codeset = CS_US_ASCII;
5621         } else if (prefs_common.outgoing_fallback_to_ascii &&
5622                    is_ascii_str(chars)) {
5623                 out_codeset = CS_US_ASCII;
5624                 encoding = ENC_7BIT;
5625         }
5626
5627         if (!out_codeset) {
5628                 gchar *test_conv_global_out = NULL;
5629                 gchar *test_conv_reply = NULL;
5630
5631                 /* automatic mode. be automatic. */
5632                 codeconv_set_strict(TRUE);
5633
5634                 out_codeset = conv_get_outgoing_charset_str();
5635                 if (out_codeset) {
5636                         debug_print("trying to convert to %s\n", out_codeset);
5637                         test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5638                 }
5639
5640                 if (!test_conv_global_out && compose->orig_charset
5641                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5642                         out_codeset = compose->orig_charset;
5643                         debug_print("failure; trying to convert to %s\n", out_codeset);
5644                         test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5645                 }
5646
5647                 if (!test_conv_global_out && !test_conv_reply) {
5648                         /* we're lost */
5649                         out_codeset = CS_INTERNAL;
5650                         debug_print("failure; finally using %s\n", out_codeset);
5651                 }
5652                 g_free(test_conv_global_out);
5653                 g_free(test_conv_reply);
5654                 codeconv_set_strict(FALSE);
5655         }
5656
5657         if (encoding == ENC_UNKNOWN) {
5658                 if (prefs_common.encoding_method == CTE_BASE64)
5659                         encoding = ENC_BASE64;
5660                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5661                         encoding = ENC_QUOTED_PRINTABLE;
5662                 else if (prefs_common.encoding_method == CTE_8BIT)
5663                         encoding = ENC_8BIT;
5664                 else
5665                         encoding = procmime_get_encoding_for_charset(out_codeset);
5666         }
5667
5668         debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5669                     src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5670
5671         if (action == COMPOSE_WRITE_FOR_SEND) {
5672                 codeconv_set_strict(TRUE);
5673                 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5674                 codeconv_set_strict(FALSE);
5675
5676                 if (!buf) {
5677                         AlertValue aval;
5678
5679                         msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5680                                                 "to the specified %s charset.\n"
5681                                                 "Send it as %s?"), out_codeset, src_codeset);
5682                         aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL,
5683                                                g_strconcat("+", _("_Send"), NULL), NULL, FALSE,
5684                                               NULL, ALERT_ERROR, G_ALERTDEFAULT);
5685                         g_free(msg);
5686
5687                         if (aval != G_ALERTALTERNATE) {
5688                                 g_free(chars);
5689                                 return -3;
5690                         } else {
5691                                 buf = chars;
5692                                 out_codeset = src_codeset;
5693                                 chars = NULL;
5694                         }
5695                 }
5696         } else {
5697                 buf = chars;
5698                 out_codeset = src_codeset;
5699                 chars = NULL;
5700         }
5701         g_free(chars);
5702
5703         if (prefs_common.rewrite_first_from && (encoding == ENC_8BIT || encoding == ENC_7BIT)) {
5704                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5705                     strstr(buf, "\nFrom ") != NULL) {
5706                         encoding = ENC_QUOTED_PRINTABLE;
5707                 }
5708         }
5709
5710         mimetext = procmime_mimeinfo_new();
5711         mimetext->content = MIMECONTENT_MEM;
5712         mimetext->tmp = TRUE; /* must free content later */
5713         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5714          * and free the data, which we need later. */
5715         mimetext->data.mem = g_strdup(buf); 
5716         mimetext->type = MIMETYPE_TEXT;
5717         mimetext->subtype = g_strdup("plain");
5718         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5719                             g_strdup(out_codeset));
5720                             
5721         /* protect trailing spaces when signing message */
5722         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5723             privacy_system_can_sign(compose->privacy_system)) {
5724                 encoding = ENC_QUOTED_PRINTABLE;
5725         }
5726
5727 #ifdef G_OS_WIN32
5728         debug_print("main text: %Id bytes encoded as %s in %d\n",
5729 #else
5730         debug_print("main text: %zd bytes encoded as %s in %d\n",
5731 #endif
5732                 strlen(buf), out_codeset, encoding);
5733
5734         /* check for line length limit */
5735         if (action == COMPOSE_WRITE_FOR_SEND &&
5736             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5737             check_line_length(buf, 1000, &line) < 0) {
5738                 AlertValue aval;
5739
5740                 msg = g_strdup_printf
5741                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5742                            "The contents of the message might be broken on the way to the delivery.\n"
5743                            "\n"
5744                            "Send it anyway?"), line + 1);
5745                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5746                 g_free(msg);
5747                 if (aval != G_ALERTALTERNATE) {
5748                         g_free(buf);
5749                         return -1;
5750                 }
5751         }
5752         
5753         if (encoding != ENC_UNKNOWN)
5754                 procmime_encode_content(mimetext, encoding);
5755
5756         /* append attachment parts */
5757         if (compose_use_attach(compose) && attach_parts) {
5758                 MimeInfo *mimempart;
5759                 gchar *boundary = NULL;
5760                 mimempart = procmime_mimeinfo_new();
5761                 mimempart->content = MIMECONTENT_EMPTY;
5762                 mimempart->type = MIMETYPE_MULTIPART;
5763                 mimempart->subtype = g_strdup("mixed");
5764
5765                 do {
5766                         g_free(boundary);
5767                         boundary = generate_mime_boundary(NULL);
5768                 } while (strstr(buf, boundary) != NULL);
5769
5770                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5771                                     boundary);
5772
5773                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5774
5775                 g_node_append(mimempart->node, mimetext->node);
5776                 g_node_append(mimemsg->node, mimempart->node);
5777
5778                 if (compose_add_attachments(compose, mimempart) < 0)
5779                         return -1;
5780         } else
5781                 g_node_append(mimemsg->node, mimetext->node);
5782
5783         g_free(buf);
5784
5785         if (strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) != 0) {
5786                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5787                 /* extract name and address */
5788                 if (strstr(spec, " <") && strstr(spec, ">")) {
5789                         from_addr = g_strdup(strrchr(spec, '<')+1);
5790                         *(strrchr(from_addr, '>')) = '\0';
5791                         from_name = g_strdup(spec);
5792                         *(strrchr(from_name, '<')) = '\0';
5793                 } else {
5794                         from_name = NULL;
5795                         from_addr = NULL;
5796                 }
5797                 g_free(spec);
5798         }
5799         /* sign message if sending */
5800         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5801             privacy_system_can_sign(compose->privacy_system))
5802                 if (!privacy_sign(compose->privacy_system, mimemsg, 
5803                         compose->account, from_addr)) {
5804                         g_free(from_name);
5805                         g_free(from_addr);
5806                         return -2;
5807         }
5808         g_free(from_name);
5809         g_free(from_addr);
5810
5811         if (compose->use_encryption) {
5812                 if (compose->encdata != NULL &&
5813                                 strcmp(compose->encdata, "_DONT_ENCRYPT_")) {
5814
5815                         /* First, write an unencrypted copy and save it to outbox, if
5816                          * user wants that. */
5817                         if (compose->account->save_encrypted_as_clear_text) {
5818                                 debug_print("saving sent message unencrypted...\n");
5819                                 FILE *tmpfp = get_tmpfile_in_dir(get_mime_tmp_dir(), &tmp_enc_file);
5820                                 if (tmpfp) {
5821                                         fclose(tmpfp);
5822
5823                                         /* fp now points to a file with headers written,
5824                                          * let's make a copy. */
5825                                         rewind(fp);
5826                                         content = file_read_stream_to_str(fp);
5827
5828                                         str_write_to_file(content, tmp_enc_file);
5829                                         g_free(content);
5830
5831                                         /* Now write the unencrypted body. */
5832                                         if ((tmpfp = g_fopen(tmp_enc_file, "a")) != NULL) {
5833                                                 procmime_write_mimeinfo(mimemsg, tmpfp);
5834                                                 fclose(tmpfp);
5835
5836                                                 outbox = folder_find_item_from_identifier(compose_get_save_to(compose));
5837                                                 if (!outbox)
5838                                                         outbox = folder_get_default_outbox();
5839
5840                                                 procmsg_save_to_outbox(outbox, tmp_enc_file, TRUE);
5841                                                 claws_unlink(tmp_enc_file);
5842                                         } else {
5843                                                 g_warning("Can't open file '%s'", tmp_enc_file);
5844                                         }
5845                                 } else {
5846                                         g_warning("couldn't get tempfile");
5847                                 }
5848                         }
5849                         if (!privacy_encrypt(compose->privacy_system, mimemsg, compose->encdata)) {
5850                                 debug_print("Couldn't encrypt mime structure: %s.\n",
5851                                                 privacy_get_error());
5852                                 alertpanel_error(_("Couldn't encrypt the email: %s"),
5853                                                 privacy_get_error());
5854                         }
5855                 }
5856         }
5857
5858         procmime_write_mimeinfo(mimemsg, fp);
5859         
5860         procmime_mimeinfo_free_all(&mimemsg);
5861
5862         return 0;
5863 }
5864
5865 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5866 {
5867         GtkTextBuffer *buffer;
5868         GtkTextIter start, end;
5869         FILE *fp;
5870         size_t len;
5871         gchar *chars, *tmp;
5872
5873         if ((fp = g_fopen(file, "wb")) == NULL) {
5874                 FILE_OP_ERROR(file, "fopen");
5875                 return -1;
5876         }
5877
5878         /* chmod for security */
5879         if (change_file_mode_rw(fp, file) < 0) {
5880                 FILE_OP_ERROR(file, "chmod");
5881                 g_warning("can't change file mode");
5882         }
5883
5884         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5885         gtk_text_buffer_get_start_iter(buffer, &start);
5886         gtk_text_buffer_get_end_iter(buffer, &end);
5887         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5888
5889         chars = conv_codeset_strdup
5890                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5891
5892         g_free(tmp);
5893         if (!chars) {
5894                 fclose(fp);
5895                 claws_unlink(file);
5896                 return -1;
5897         }
5898         /* write body */
5899         len = strlen(chars);
5900         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5901                 FILE_OP_ERROR(file, "fwrite");
5902                 g_free(chars);
5903                 fclose(fp);
5904                 claws_unlink(file);
5905                 return -1;
5906         }
5907
5908         g_free(chars);
5909
5910         if (fclose(fp) == EOF) {
5911                 FILE_OP_ERROR(file, "fclose");
5912                 claws_unlink(file);
5913                 return -1;
5914         }
5915         return 0;
5916 }
5917
5918 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5919 {
5920         FolderItem *item;
5921         MsgInfo *msginfo = compose->targetinfo;
5922
5923         cm_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5924         if (!msginfo) return -1;
5925
5926         if (!force && MSG_IS_LOCKED(msginfo->flags))
5927                 return 0;
5928
5929         item = msginfo->folder;
5930         cm_return_val_if_fail(item != NULL, -1);
5931
5932         if (procmsg_msg_exist(msginfo) &&
5933             (folder_has_parent_of_type(item, F_QUEUE) ||
5934              folder_has_parent_of_type(item, F_DRAFT) 
5935              || msginfo == compose->autosaved_draft)) {
5936                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5937                         g_warning("can't remove the old message");
5938                         return -1;
5939                 } else {
5940                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5941                 }
5942         }
5943
5944         return 0;
5945 }
5946
5947 static void compose_remove_draft(Compose *compose)
5948 {
5949         FolderItem *drafts;
5950         MsgInfo *msginfo = compose->targetinfo;
5951         drafts = account_get_special_folder(compose->account, F_DRAFT);
5952
5953         if (procmsg_msg_exist(msginfo)) {
5954                 folder_item_remove_msg(drafts, msginfo->msgnum);
5955         }
5956
5957 }
5958
5959 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5960                    gboolean remove_reedit_target)
5961 {
5962         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5963 }
5964
5965 static gboolean compose_warn_encryption(Compose *compose)
5966 {
5967         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5968         AlertValue val = G_ALERTALTERNATE;
5969         
5970         if (warning == NULL)
5971                 return TRUE;
5972
5973         val = alertpanel_full(_("Encryption warning"), warning,
5974                   GTK_STOCK_CANCEL, g_strconcat("+", _("C_ontinue"), NULL), NULL,
5975                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5976         if (val & G_ALERTDISABLE) {
5977                 val &= ~G_ALERTDISABLE;
5978                 if (val == G_ALERTALTERNATE)
5979                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5980                                 TRUE);
5981         }
5982
5983         if (val == G_ALERTALTERNATE) {
5984                 return TRUE;
5985         } else {
5986                 return FALSE;
5987         } 
5988 }
5989
5990 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5991                               gchar **msgpath, gboolean perform_checks,
5992                               gboolean remove_reedit_target)
5993 {
5994         FolderItem *queue;
5995         gchar *tmp;
5996         FILE *fp;
5997         GSList *cur;
5998         gint num;
5999         PrefsAccount *mailac = NULL, *newsac = NULL;
6000         gboolean err = FALSE;
6001
6002         debug_print("queueing message...\n");
6003         cm_return_val_if_fail(compose->account != NULL, -1);
6004
6005         if (compose_check_entries(compose, perform_checks) == FALSE) {
6006                 if (compose->batch) {
6007                         gtk_widget_show_all(compose->window);
6008                 }
6009                 return -1;
6010         }
6011
6012         if (!compose->to_list && !compose->newsgroup_list) {
6013                 g_warning("can't get recipient list.");
6014                 return -1;
6015         }
6016
6017         if (compose->to_list) {
6018                 if (compose->account->protocol != A_NNTP)
6019                         mailac = compose->account;
6020                 else if (cur_account && cur_account->protocol != A_NNTP)
6021                         mailac = cur_account;
6022                 else if (!(mailac = compose_current_mail_account())) {
6023                         alertpanel_error(_("No account for sending mails available!"));
6024                         return -1;
6025                 }
6026         }
6027
6028         if (compose->newsgroup_list) {
6029                 if (compose->account->protocol == A_NNTP)
6030                         newsac = compose->account;
6031                 else {
6032                         alertpanel_error(_("Selected account isn't NNTP: Posting is impossible."));
6033                         return -1;
6034                 }                       
6035         }
6036
6037         /* write queue header */
6038         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
6039                               G_DIR_SEPARATOR, compose, (guint) rand());
6040         debug_print("queuing to %s\n", tmp);
6041         if ((fp = g_fopen(tmp, "w+b")) == NULL) {
6042                 FILE_OP_ERROR(tmp, "fopen");
6043                 g_free(tmp);
6044                 return -2;
6045         }
6046
6047         if (change_file_mode_rw(fp, tmp) < 0) {
6048                 FILE_OP_ERROR(tmp, "chmod");
6049                 g_warning("can't change file mode");
6050         }
6051
6052         /* queueing variables */
6053         err |= (fprintf(fp, "AF:\n") < 0);
6054         err |= (fprintf(fp, "NF:0\n") < 0);
6055         err |= (fprintf(fp, "PS:10\n") < 0);
6056         err |= (fprintf(fp, "SRH:1\n") < 0);
6057         err |= (fprintf(fp, "SFN:\n") < 0);
6058         err |= (fprintf(fp, "DSR:\n") < 0);
6059         if (compose->msgid)
6060                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
6061         else
6062                 err |= (fprintf(fp, "MID:\n") < 0);
6063         err |= (fprintf(fp, "CFG:\n") < 0);
6064         err |= (fprintf(fp, "PT:0\n") < 0);
6065         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
6066         err |= (fprintf(fp, "RQ:\n") < 0);
6067         if (mailac)
6068                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
6069         else
6070                 err |= (fprintf(fp, "SSV:\n") < 0);
6071         if (newsac)
6072                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
6073         else
6074                 err |= (fprintf(fp, "NSV:\n") < 0);
6075         err |= (fprintf(fp, "SSH:\n") < 0);
6076         /* write recepient list */
6077         if (compose->to_list) {
6078                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
6079                 for (cur = compose->to_list->next; cur != NULL;
6080                      cur = cur->next)
6081                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
6082                 err |= (fprintf(fp, "\n") < 0);
6083         }
6084         /* write newsgroup list */
6085         if (compose->newsgroup_list) {
6086                 err |= (fprintf(fp, "NG:") < 0);
6087                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
6088                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
6089                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
6090                 err |= (fprintf(fp, "\n") < 0);
6091         }
6092         /* Sylpheed account IDs */
6093         if (mailac)
6094                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
6095         if (newsac)
6096                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
6097
6098         
6099         if (compose->privacy_system != NULL) {
6100                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
6101                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
6102                 if (compose->use_encryption) {
6103                         if (!compose_warn_encryption(compose)) {
6104                                 fclose(fp);
6105                                 claws_unlink(tmp);
6106                                 g_free(tmp);
6107                                 return -6;
6108                         }
6109                         if (mailac && mailac->encrypt_to_self) {
6110                                 GSList *tmp_list = g_slist_copy(compose->to_list);
6111                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
6112                                 compose->encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
6113                                 g_slist_free(tmp_list);
6114                         } else {
6115                                 compose->encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
6116                         }
6117                         if (compose->encdata != NULL) {
6118                                 if (strcmp(compose->encdata, "_DONT_ENCRYPT_")) {
6119                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
6120                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
6121                                                 compose->encdata) < 0);
6122                                 } /* else we finally dont want to encrypt */
6123                         } else {
6124                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
6125                                 /* and if encdata was null, it means there's been a problem in 
6126                                  * key selection */
6127                                 if (err == TRUE)
6128                                         g_warning("failed to write queue message");
6129                                 fclose(fp);
6130                                 claws_unlink(tmp);
6131                                 g_free(tmp);
6132                                 return -5;
6133                         }
6134                 }
6135         }
6136
6137         /* Save copy folder */
6138         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
6139                 gchar *savefolderid;
6140                 
6141                 savefolderid = compose_get_save_to(compose);
6142                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
6143                 g_free(savefolderid);
6144         }
6145         /* Save copy folder */
6146         if (compose->return_receipt) {
6147                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
6148         }
6149         /* Message-ID of message replying to */
6150         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
6151                 gchar *folderid = NULL;
6152
6153                 if (compose->replyinfo->folder)
6154                         folderid = folder_item_get_identifier(compose->replyinfo->folder);
6155                 if (folderid == NULL)
6156                         folderid = g_strdup("NULL");
6157
6158                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
6159                 g_free(folderid);
6160         }
6161         /* Message-ID of message forwarding to */
6162         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
6163                 gchar *folderid = NULL;
6164                 
6165                 if (compose->fwdinfo->folder)
6166                         folderid = folder_item_get_identifier(compose->fwdinfo->folder);
6167                 if (folderid == NULL)
6168                         folderid = g_strdup("NULL");
6169
6170                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
6171                 g_free(folderid);
6172         }
6173
6174         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
6175         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
6176
6177         /* end of headers */
6178         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
6179
6180         if (compose->redirect_filename != NULL) {
6181                 if (compose_redirect_write_to_file(compose, fp) < 0) {
6182                         fclose(fp);
6183                         claws_unlink(tmp);
6184                         g_free(tmp);
6185                         return -2;
6186                 }
6187         } else {
6188                 gint result = 0;
6189                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
6190                         fclose(fp);
6191                         claws_unlink(tmp);
6192                         g_free(tmp);
6193                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
6194                 }
6195         }
6196         if (err == TRUE) {
6197                 g_warning("failed to write queue message");
6198                 fclose(fp);
6199                 claws_unlink(tmp);
6200                 g_free(tmp);
6201                 return -2;
6202         }
6203         if (fclose(fp) == EOF) {
6204                 FILE_OP_ERROR(tmp, "fclose");
6205                 claws_unlink(tmp);
6206                 g_free(tmp);
6207                 return -2;
6208         }
6209
6210         if (item && *item) {
6211                 queue = *item;
6212         } else {
6213                 queue = account_get_special_folder(compose->account, F_QUEUE);
6214         }
6215         if (!queue) {
6216                 g_warning("can't find queue folder");
6217                 claws_unlink(tmp);
6218                 g_free(tmp);
6219                 return -1;
6220         }
6221         folder_item_scan(queue);
6222         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
6223                 g_warning("can't queue the message");
6224                 claws_unlink(tmp);
6225                 g_free(tmp);
6226                 return -1;
6227         }
6228         
6229         if (msgpath == NULL) {
6230                 claws_unlink(tmp);
6231                 g_free(tmp);
6232         } else
6233                 *msgpath = tmp;
6234
6235         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
6236                 compose_remove_reedit_target(compose, FALSE);
6237         }
6238
6239         if ((msgnum != NULL) && (item != NULL)) {
6240                 *msgnum = num;
6241                 *item = queue;
6242         }
6243
6244         return 0;
6245 }
6246
6247 static int compose_add_attachments(Compose *compose, MimeInfo *parent)
6248 {
6249         AttachInfo *ainfo;
6250         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
6251         MimeInfo *mimepart;
6252         GStatBuf statbuf;
6253         gchar *type, *subtype;
6254         GtkTreeModel *model;
6255         GtkTreeIter iter;
6256
6257         model = gtk_tree_view_get_model(tree_view);
6258         
6259         if (!gtk_tree_model_get_iter_first(model, &iter))
6260                 return 0;
6261         do {
6262                 gtk_tree_model_get(model, &iter,
6263                                    COL_DATA, &ainfo,
6264                                    -1);
6265                 
6266                 if (!is_file_exist(ainfo->file)) {
6267                         gchar *msg = g_strdup_printf(_("Attachment %s doesn't exist anymore. Ignore?"), ainfo->file);
6268                         AlertValue val = alertpanel_full(_("Warning"), msg, _("Cancel sending"), _("Ignore attachment"),
6269                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
6270                         g_free(msg);
6271                         if (val == G_ALERTDEFAULT) {
6272                                 return -1;
6273                         }
6274                         continue;
6275                 }
6276                 if (g_stat(ainfo->file, &statbuf) < 0)
6277                         return -1;
6278
6279                 mimepart = procmime_mimeinfo_new();
6280                 mimepart->content = MIMECONTENT_FILE;
6281                 mimepart->data.filename = g_strdup(ainfo->file);
6282                 mimepart->tmp = FALSE; /* or we destroy our attachment */
6283                 mimepart->offset = 0;
6284                 mimepart->length = statbuf.st_size;
6285
6286                 type = g_strdup(ainfo->content_type);
6287
6288                 if (!strchr(type, '/')) {
6289                         g_free(type);
6290                         type = g_strdup("application/octet-stream");
6291                 }
6292
6293                 subtype = strchr(type, '/') + 1;
6294                 *(subtype - 1) = '\0';
6295                 mimepart->type = procmime_get_media_type(type);
6296                 mimepart->subtype = g_strdup(subtype);
6297                 g_free(type);
6298
6299                 if (mimepart->type == MIMETYPE_MESSAGE && 
6300                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
6301                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
6302                 } else if (mimepart->type == MIMETYPE_TEXT) {
6303                         if (!ainfo->name && g_ascii_strcasecmp(mimepart->subtype, "plain")) {
6304                                 /* Text parts with no name come from multipart/alternative
6305                                 * forwards. Make sure the recipient won't look at the 
6306                                 * original HTML part by mistake. */
6307                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6308                                 ainfo->name = g_strdup_printf(_("Original %s part"),
6309                                                                 mimepart->subtype);
6310                         }
6311                         if (ainfo->charset)
6312                                 g_hash_table_insert(mimepart->typeparameters,
6313                                                     g_strdup("charset"), g_strdup(ainfo->charset));
6314                 }
6315                 if (ainfo->name && mimepart->type != MIMETYPE_MESSAGE) {
6316                         if (mimepart->type == MIMETYPE_APPLICATION && 
6317                            !strcmp2(mimepart->subtype, "octet-stream"))
6318                                 g_hash_table_insert(mimepart->typeparameters,
6319                                                 g_strdup("name"), g_strdup(ainfo->name));
6320                         g_hash_table_insert(mimepart->dispositionparameters,
6321                                         g_strdup("filename"), g_strdup(ainfo->name));
6322                         mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
6323                 }
6324
6325                 if (mimepart->type == MIMETYPE_MESSAGE
6326                     || mimepart->type == MIMETYPE_MULTIPART)
6327                         ainfo->encoding = ENC_BINARY;
6328                 else if (compose->use_signing) {
6329                         if (ainfo->encoding == ENC_7BIT)
6330                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
6331                         else if (ainfo->encoding == ENC_8BIT)
6332                                 ainfo->encoding = ENC_BASE64;
6333                 }
6334
6335                 
6336                 
6337                 procmime_encode_content(mimepart, ainfo->encoding);
6338
6339                 g_node_append(parent->node, mimepart->node);
6340         } while (gtk_tree_model_iter_next(model, &iter));
6341         
6342         return 0;
6343 }
6344
6345 static gchar *compose_quote_list_of_addresses(gchar *str)
6346 {
6347         GSList *list = NULL, *item = NULL;
6348         gchar *qname = NULL, *faddr = NULL, *result = NULL;
6349
6350         list = address_list_append_with_comments(list, str);
6351         for (item = list; item != NULL; item = item->next) {
6352                 gchar *spec = item->data;
6353                 gchar *endofname = strstr(spec, " <");
6354                 if (endofname != NULL) {
6355                         gchar * qqname;
6356                         *endofname = '\0';
6357                         QUOTE_IF_REQUIRED_NORMAL(qname, spec, return NULL);
6358                         qqname = escape_internal_quotes(qname, '"');
6359                         *endofname = ' ';
6360                         if (*qname != *spec || qqname != qname) { /* has been quoted, compute new */
6361                                 gchar *addr = g_strdup(endofname);
6362                                 gchar *name = (qqname != qname)? qqname: g_strdup(qname);
6363                                 faddr = g_strconcat(name, addr, NULL);
6364                                 g_free(name);
6365                                 g_free(addr);
6366                                 debug_print("new auto-quoted address: '%s'\n", faddr);
6367                         }
6368                 }
6369                 if (result == NULL)
6370                         result = g_strdup((faddr != NULL)? faddr: spec);
6371                 else {
6372                         result = g_strconcat(result,
6373                                              ", ",
6374                                              (faddr != NULL)? faddr: spec,
6375                                              NULL);
6376                 }
6377                 if (faddr != NULL) {
6378                         g_free(faddr);
6379                         faddr = NULL;
6380                 }
6381         }
6382         slist_free_strings_full(list);
6383
6384         return result;
6385 }
6386
6387 #define IS_IN_CUSTOM_HEADER(header) \
6388         (compose->account->add_customhdr && \
6389          custom_header_find(compose->account->customhdr_list, header) != NULL)
6390
6391 static void compose_add_headerfield_from_headerlist(Compose *compose, 
6392                                                     GString *header, 
6393                                                     const gchar *fieldname,
6394                                                     const gchar *seperator)
6395 {
6396         gchar *str, *fieldname_w_colon;
6397         gboolean add_field = FALSE;
6398         GSList *list;
6399         ComposeHeaderEntry *headerentry;
6400         const gchar *headerentryname;
6401         const gchar *trans_fieldname;
6402         GString *fieldstr;
6403
6404         if (IS_IN_CUSTOM_HEADER(fieldname))
6405                 return;
6406
6407         debug_print("Adding %s-fields\n", fieldname);
6408
6409         fieldstr = g_string_sized_new(64);
6410
6411         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
6412         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
6413
6414         for (list = compose->header_list; list; list = list->next) {
6415                 headerentry = ((ComposeHeaderEntry *)list->data);
6416                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
6417
6418                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
6419                         gchar * ustr = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
6420                         g_strstrip(ustr);
6421                         str = compose_quote_list_of_addresses(ustr);
6422                         g_free(ustr);
6423                         if (str != NULL && str[0] != '\0') {
6424                                 if (add_field)
6425                                         g_string_append(fieldstr, seperator);
6426                                 g_string_append(fieldstr, str);
6427                                 add_field = TRUE;
6428                         }
6429                         g_free(str);
6430                 }
6431         }
6432         if (add_field) {
6433                 gchar *buf;
6434
6435                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
6436                 compose_convert_header
6437                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
6438                         strlen(fieldname) + 2, TRUE);
6439                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
6440                 g_free(buf);
6441         }
6442
6443         g_free(fieldname_w_colon);
6444         g_string_free(fieldstr, TRUE);
6445
6446         return;
6447 }
6448
6449 static gchar *compose_get_manual_headers_info(Compose *compose)
6450 {
6451         GString *sh_header = g_string_new(" ");
6452         GSList *list;
6453         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6454
6455         for (list = compose->header_list; list; list = list->next) {
6456                 ComposeHeaderEntry *headerentry;
6457                 gchar *tmp;
6458                 gchar *headername;
6459                 gchar *headername_wcolon;
6460                 const gchar *headername_trans;
6461                 gchar **string;
6462                 gboolean standard_header = FALSE;
6463
6464                 headerentry = ((ComposeHeaderEntry *)list->data);
6465
6466                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6467                 g_strstrip(tmp);
6468                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6469                         g_free(tmp);
6470                         continue;
6471                 }
6472
6473                 if (!strstr(tmp, ":")) {
6474                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6475                         headername = g_strdup(tmp);
6476                 } else {
6477                         headername_wcolon = g_strdup(tmp);
6478                         headername = g_strdup(strtok(tmp, ":"));
6479                 }
6480                 g_free(tmp);
6481                 
6482                 string = std_headers;
6483                 while (*string != NULL) {
6484                         headername_trans = prefs_common_translated_header_name(*string);
6485                         if (!strcmp(headername_trans, headername_wcolon))
6486                                 standard_header = TRUE;
6487                         string++;
6488                 }
6489                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6490                         g_string_append_printf(sh_header, "%s ", headername);
6491                 g_free(headername);
6492                 g_free(headername_wcolon);
6493         }
6494         g_string_truncate(sh_header, strlen(sh_header->str) - 1); /* remove last space */
6495         return g_string_free(sh_header, FALSE);
6496 }
6497
6498 static gchar *compose_get_header(Compose *compose)
6499 {
6500         gchar buf[BUFFSIZE];
6501         const gchar *entry_str;
6502         gchar *str;
6503         gchar *name;
6504         GSList *list;
6505         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6506         GString *header;
6507         gchar *from_name = NULL, *from_address = NULL;
6508         gchar *tmp;
6509
6510         cm_return_val_if_fail(compose->account != NULL, NULL);
6511         cm_return_val_if_fail(compose->account->address != NULL, NULL);
6512
6513         header = g_string_sized_new(64);
6514
6515         /* Date */
6516         if (prefs_common.hide_timezone)
6517                 get_rfc822_date_hide_tz(buf, sizeof(buf));
6518         else
6519                 get_rfc822_date(buf, sizeof(buf));
6520         g_string_append_printf(header, "Date: %s\n", buf);
6521
6522         /* From */
6523         
6524         if (compose->account->name && *compose->account->name) {
6525                 gchar *buf;
6526                 QUOTE_IF_REQUIRED(buf, compose->account->name);
6527                 tmp = g_strdup_printf("%s <%s>",
6528                         buf, compose->account->address);
6529         } else {
6530                 tmp = g_strdup_printf("%s",
6531                         compose->account->address);
6532         }
6533         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
6534         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
6535                 /* use default */
6536                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
6537                 from_address = g_strdup(compose->account->address);
6538         } else {
6539                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
6540                 /* extract name and address */
6541                 if (strstr(spec, " <") && strstr(spec, ">")) {
6542                         from_address = g_strdup(strrchr(spec, '<')+1);
6543                         *(strrchr(from_address, '>')) = '\0';
6544                         from_name = g_strdup(spec);
6545                         *(strrchr(from_name, '<')) = '\0';
6546                 } else {
6547                         from_name = NULL;
6548                         from_address = g_strdup(spec);
6549                 }
6550                 g_free(spec);
6551         }
6552         g_free(tmp);
6553         
6554         
6555         if (from_name && *from_name) {
6556                 gchar *qname;
6557                 compose_convert_header
6558                         (compose, buf, sizeof(buf), from_name,
6559                          strlen("From: "), TRUE);
6560                 QUOTE_IF_REQUIRED(name, buf);
6561                 qname = escape_internal_quotes(name, '"');
6562                 
6563                 g_string_append_printf(header, "From: %s <%s>\n",
6564                         qname, from_address);
6565                 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6566                     compose->return_receipt) {
6567                         compose_convert_header(compose, buf, sizeof(buf), from_name,
6568                                                strlen("Disposition-Notification-To: "),
6569                                                TRUE);
6570                         g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, from_address);
6571                 }
6572                 if (qname != name)
6573                         g_free(qname);
6574         } else {
6575                 g_string_append_printf(header, "From: %s\n", from_address);
6576                 if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To") &&
6577                     compose->return_receipt)
6578                         g_string_append_printf(header, "Disposition-Notification-To: %s\n", from_address);
6579
6580         }
6581         g_free(from_name);
6582         g_free(from_address);
6583
6584         /* To */
6585         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
6586
6587         /* Newsgroups */
6588         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
6589
6590         /* Cc */
6591         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
6592
6593         /* Bcc */
6594         /* 
6595          * If this account is a NNTP account remove Bcc header from 
6596          * message body since it otherwise will be publicly shown
6597          */
6598         if (compose->account->protocol != A_NNTP)
6599                 compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
6600
6601         /* Subject */
6602         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
6603
6604         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
6605                 g_strstrip(str);
6606                 if (*str != '\0') {
6607                         compose_convert_header(compose, buf, sizeof(buf), str,
6608                                                strlen("Subject: "), FALSE);
6609                         g_string_append_printf(header, "Subject: %s\n", buf);
6610                 }
6611         }
6612         g_free(str);
6613
6614         /* Message-ID */
6615         if (compose->msgid != NULL && strlen(compose->msgid) > 0) {
6616                 g_string_append_printf(header, "Message-ID: <%s>\n",
6617                                 compose->msgid);
6618         }
6619
6620         if (compose->remove_references == FALSE) {
6621                 /* In-Reply-To */
6622                 if (compose->inreplyto && compose->to_list)
6623                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
6624         
6625                 /* References */
6626                 if (compose->references)
6627                         g_string_append_printf(header, "References: %s\n", compose->references);
6628         }
6629
6630         /* Followup-To */
6631         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
6632
6633         /* Reply-To */
6634         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
6635
6636         /* Organization */
6637         if (compose->account->organization &&
6638             strlen(compose->account->organization) &&
6639             !IS_IN_CUSTOM_HEADER("Organization")) {
6640                 compose_convert_header(compose, buf, sizeof(buf),
6641                                        compose->account->organization,
6642                                        strlen("Organization: "), FALSE);
6643                 g_string_append_printf(header, "Organization: %s\n", buf);
6644         }
6645
6646         /* Program version and system info */
6647         if (compose->account->gen_xmailer &&
6648             g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
6649             !compose->newsgroup_list) {
6650                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
6651                         prog_version,
6652                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6653                         TARGET_ALIAS);
6654         }
6655         if (compose->account->gen_xmailer &&
6656             g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
6657                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
6658                         prog_version,
6659                         gtk_major_version, gtk_minor_version, gtk_micro_version,
6660                         TARGET_ALIAS);
6661         }
6662
6663         /* custom headers */
6664         if (compose->account->add_customhdr) {
6665                 GSList *cur;
6666
6667                 for (cur = compose->account->customhdr_list; cur != NULL;
6668                      cur = cur->next) {
6669                         CustomHeader *chdr = (CustomHeader *)cur->data;
6670
6671                         if (custom_header_is_allowed(chdr->name)
6672                             && chdr->value != NULL
6673                             && *(chdr->value) != '\0') {
6674                                 compose_convert_header
6675                                         (compose, buf, sizeof(buf),
6676                                          chdr->value,
6677                                          strlen(chdr->name) + 2, FALSE);
6678                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
6679                         }
6680                 }
6681         }
6682
6683         /* Automatic Faces and X-Faces */
6684         if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6685                 g_string_append_printf(header, "X-Face: %s\n", buf);
6686         }
6687         else if (get_default_xface (buf, sizeof(buf)) == 0) {
6688                 g_string_append_printf(header, "X-Face: %s\n", buf);
6689         }
6690         if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6691                 g_string_append_printf(header, "Face: %s\n", buf);
6692         }
6693         else if (get_default_face (buf, sizeof(buf)) == 0) {
6694                 g_string_append_printf(header, "Face: %s\n", buf);
6695         }
6696
6697         /* PRIORITY */
6698         switch (compose->priority) {
6699                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6700                                                    "X-Priority: 1 (Highest)\n");
6701                         break;
6702                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6703                                                 "X-Priority: 2 (High)\n");
6704                         break;
6705                 case PRIORITY_NORMAL: break;
6706                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6707                                                "X-Priority: 4 (Low)\n");
6708                         break;
6709                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6710                                                   "X-Priority: 5 (Lowest)\n");
6711                         break;
6712                 default: debug_print("compose: priority unknown : %d\n",
6713                                      compose->priority);
6714         }
6715
6716         /* get special headers */
6717         for (list = compose->header_list; list; list = list->next) {
6718                 ComposeHeaderEntry *headerentry;
6719                 gchar *tmp;
6720                 gchar *headername;
6721                 gchar *headername_wcolon;
6722                 const gchar *headername_trans;
6723                 gchar *headervalue;
6724                 gchar **string;
6725                 gboolean standard_header = FALSE;
6726
6727                 headerentry = ((ComposeHeaderEntry *)list->data);
6728
6729                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6730                 g_strstrip(tmp);
6731                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6732                         g_free(tmp);
6733                         continue;
6734                 }
6735
6736                 if (!strstr(tmp, ":")) {
6737                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6738                         headername = g_strdup(tmp);
6739                 } else {
6740                         headername_wcolon = g_strdup(tmp);
6741                         headername = g_strdup(strtok(tmp, ":"));
6742                 }
6743                 g_free(tmp);
6744                 
6745                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6746                 Xstrdup_a(headervalue, entry_str, return NULL);
6747                 subst_char(headervalue, '\r', ' ');
6748                 subst_char(headervalue, '\n', ' ');
6749                 string = std_headers;
6750                 while (*string != NULL) {
6751                         headername_trans = prefs_common_translated_header_name(*string);
6752                         if (!strcmp(headername_trans, headername_wcolon))
6753                                 standard_header = TRUE;
6754                         string++;
6755                 }
6756                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6757                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6758                                 
6759                 g_free(headername);
6760                 g_free(headername_wcolon);              
6761         }
6762
6763         str = header->str;
6764         g_string_free(header, FALSE);
6765
6766         return str;
6767 }
6768
6769 #undef IS_IN_CUSTOM_HEADER
6770
6771 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6772                                    gint header_len, gboolean addr_field)
6773 {
6774         gchar *tmpstr = NULL;
6775         const gchar *out_codeset = NULL;
6776
6777         cm_return_if_fail(src != NULL);
6778         cm_return_if_fail(dest != NULL);
6779
6780         if (len < 1) return;
6781
6782         tmpstr = g_strdup(src);
6783
6784         subst_char(tmpstr, '\n', ' ');
6785         subst_char(tmpstr, '\r', ' ');
6786         g_strchomp(tmpstr);
6787
6788         if (!g_utf8_validate(tmpstr, -1, NULL)) {
6789                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6790                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6791                 g_free(tmpstr);
6792                 tmpstr = mybuf;
6793         }
6794
6795         codeconv_set_strict(TRUE);
6796         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6797                 conv_get_charset_str(compose->out_encoding));
6798         codeconv_set_strict(FALSE);
6799         
6800         if (!dest || *dest == '\0') {
6801                 gchar *test_conv_global_out = NULL;
6802                 gchar *test_conv_reply = NULL;
6803
6804                 /* automatic mode. be automatic. */
6805                 codeconv_set_strict(TRUE);
6806
6807                 out_codeset = conv_get_outgoing_charset_str();
6808                 if (out_codeset) {
6809                         debug_print("trying to convert to %s\n", out_codeset);
6810                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6811                 }
6812
6813                 if (!test_conv_global_out && compose->orig_charset
6814                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
6815                         out_codeset = compose->orig_charset;
6816                         debug_print("failure; trying to convert to %s\n", out_codeset);
6817                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6818                 }
6819
6820                 if (!test_conv_global_out && !test_conv_reply) {
6821                         /* we're lost */
6822                         out_codeset = CS_INTERNAL;
6823                         debug_print("finally using %s\n", out_codeset);
6824                 }
6825                 g_free(test_conv_global_out);
6826                 g_free(test_conv_reply);
6827                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6828                                         out_codeset);
6829                 codeconv_set_strict(FALSE);
6830         }
6831         g_free(tmpstr);
6832 }
6833
6834 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6835 {
6836         gchar *address;
6837
6838         cm_return_if_fail(user_data != NULL);
6839
6840         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6841         g_strstrip(address);
6842         if (*address != '\0') {
6843                 gchar *name = procheader_get_fromname(address);
6844                 extract_address(address);
6845 #ifndef USE_ALT_ADDRBOOK
6846                 addressbook_add_contact(name, address, NULL, NULL);
6847 #else
6848                 debug_print("%s: %s\n", name, address);
6849                 if (addressadd_selection(name, address, NULL, NULL)) {
6850                         debug_print( "addressbook_add_contact - added\n" );
6851                 }
6852 #endif
6853         }
6854         g_free(address);
6855 }
6856
6857 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6858 {
6859         GtkWidget *menuitem;
6860         gchar *address;
6861
6862         cm_return_if_fail(menu != NULL);
6863         cm_return_if_fail(GTK_IS_MENU_SHELL(menu));
6864
6865         menuitem = gtk_separator_menu_item_new();
6866         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6867         gtk_widget_show(menuitem);
6868
6869         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6870         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6871
6872         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6873         g_strstrip(address);
6874         if (*address == '\0') {
6875                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6876         }
6877
6878         g_signal_connect(G_OBJECT(menuitem), "activate",
6879                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
6880         gtk_widget_show(menuitem);
6881 }
6882
6883 void compose_add_extra_header(gchar *header, GtkListStore *model)
6884 {
6885         GtkTreeIter iter;
6886         if (strcmp(header, "")) {
6887                 COMBOBOX_ADD(model, header, COMPOSE_TO);
6888         }
6889 }
6890
6891 void compose_add_extra_header_entries(GtkListStore *model)
6892 {
6893         FILE *exh;
6894         gchar *exhrc;
6895         gchar buf[BUFFSIZE];
6896         gint lastc;
6897
6898         if (extra_headers == NULL) {
6899                 exhrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "extraheaderrc", NULL);
6900                 if ((exh = g_fopen(exhrc, "rb")) == NULL) {
6901                         debug_print("extra headers file not found\n");
6902                         goto extra_headers_done;
6903                 }
6904                 while (fgets(buf, BUFFSIZE, exh) != NULL) {
6905                         lastc = strlen(buf) - 1;        /* remove trailing control chars */
6906                         while (lastc >= 0 && buf[lastc] != ':')
6907                                 buf[lastc--] = '\0';
6908                         if (lastc > 0 && buf[0] != '#' && buf[lastc] == ':') {
6909                                 buf[lastc] = '\0'; /* remove trailing : for comparison */
6910                                 if (custom_header_is_allowed(buf)) {
6911                                         buf[lastc] = ':';
6912                                         extra_headers = g_slist_prepend(extra_headers, g_strdup(buf));
6913                                 }
6914                                 else
6915                                         g_message("disallowed extra header line: %s\n", buf);
6916                         }
6917                         else {
6918                                 if (buf[0] != '#')
6919                                         g_message("invalid extra header line: %s\n", buf);
6920                         }
6921                 }
6922                 fclose(exh);
6923 extra_headers_done:
6924                 g_free(exhrc);
6925                 extra_headers = g_slist_prepend(extra_headers, g_strdup("")); /* end of list */
6926                 extra_headers = g_slist_reverse(extra_headers);
6927         }
6928         g_slist_foreach(extra_headers, (GFunc)compose_add_extra_header, (gpointer)model);
6929 }
6930
6931 static void compose_create_header_entry(Compose *compose) 
6932 {
6933         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6934
6935         GtkWidget *combo;
6936         GtkWidget *entry;
6937         GtkWidget *button;
6938         GtkWidget *hbox;
6939         gchar **string;
6940         const gchar *header = NULL;
6941         ComposeHeaderEntry *headerentry;
6942         gboolean standard_header = FALSE;
6943         GtkListStore *model;
6944         GtkTreeIter iter;
6945         
6946         headerentry = g_new0(ComposeHeaderEntry, 1);
6947
6948         /* Combo box model */
6949         model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_BOOLEAN);
6950 #if !GTK_CHECK_VERSION(2, 24, 0)
6951         combo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
6952 #endif
6953         COMBOBOX_ADD(model, prefs_common_translated_header_name("To:"),
6954                         COMPOSE_TO);
6955         COMBOBOX_ADD(model, prefs_common_translated_header_name("Cc:"),
6956                         COMPOSE_CC);
6957         COMBOBOX_ADD(model, prefs_common_translated_header_name("Bcc:"),
6958                         COMPOSE_BCC);
6959         COMBOBOX_ADD(model, prefs_common_translated_header_name("Newsgroups:"),
6960                         COMPOSE_NEWSGROUPS);                    
6961         COMBOBOX_ADD(model, prefs_common_translated_header_name("Reply-To:"),
6962                         COMPOSE_REPLYTO);
6963         COMBOBOX_ADD(model, prefs_common_translated_header_name("Followup-To:"),
6964                         COMPOSE_FOLLOWUPTO);
6965         compose_add_extra_header_entries(model);
6966
6967         /* Combo box */
6968 #if GTK_CHECK_VERSION(2, 24, 0)
6969         combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(model));
6970         GtkCellRenderer *cell = gtk_cell_renderer_text_new();
6971         gtk_cell_renderer_set_alignment(cell, 0.0, 0.5);
6972         gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
6973         gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(combo), 0);
6974 #endif
6975         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6976         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(combo))), "grab_focus",
6977                          G_CALLBACK(compose_grab_focus_cb), compose);
6978         gtk_widget_show(combo);
6979
6980         /* Putting only the combobox child into focus chain of its parent causes
6981          * the parent to be skipped when changing focus via Tab or Shift+Tab.
6982          * This eliminates need to pres Tab twice in order to really get from the
6983          * combobox to next widget. */
6984         GList *l = NULL;
6985         l = g_list_prepend(l, gtk_bin_get_child(GTK_BIN(combo)));
6986         gtk_container_set_focus_chain(GTK_CONTAINER(combo), l);
6987         g_list_free(l);
6988
6989         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6990                         compose->header_nextrow, compose->header_nextrow+1,
6991                         GTK_SHRINK, GTK_FILL, 0, 0);
6992         if (compose->header_last && (compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN)) {
6993                 const gchar *last_header_entry = gtk_entry_get_text(
6994                                 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6995                 string = headers;
6996                 while (*string != NULL) {
6997                         if (!strcmp(prefs_common_translated_header_name(*string), last_header_entry))
6998                                 standard_header = TRUE;
6999                         string++;
7000                 }
7001                 if (standard_header)
7002                         header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
7003         }
7004         if (!compose->header_last || !standard_header) {
7005                 switch(compose->account->protocol) {
7006                         case A_NNTP:
7007                                 header = prefs_common_translated_header_name("Newsgroups:");
7008                                 break;
7009                         default:
7010                                 header = prefs_common_translated_header_name("To:");
7011                                 break;
7012                 }                                                                   
7013         }
7014         if (header)
7015                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
7016
7017         gtk_editable_set_editable(
7018                 GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo)))),
7019                 prefs_common.type_any_header);
7020
7021         g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
7022                          G_CALLBACK(compose_grab_focus_cb), compose);
7023
7024         /* Entry field with cleanup button */
7025         button = gtk_button_new();
7026         gtk_button_set_image(GTK_BUTTON(button),
7027                         gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
7028         gtk_widget_show(button);
7029         CLAWS_SET_TIP(button,
7030                 _("Delete entry contents"));
7031         entry = gtk_entry_new(); 
7032         gtk_widget_show(entry);
7033         CLAWS_SET_TIP(entry,
7034                 _("Use <tab> to autocomplete from addressbook"));
7035         hbox = gtk_hbox_new (FALSE, 0);
7036         gtk_widget_show(hbox);
7037         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
7038         gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
7039         gtk_table_attach(GTK_TABLE(compose->header_table), hbox, 1, 2,
7040                         compose->header_nextrow, compose->header_nextrow+1,
7041                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
7042
7043         g_signal_connect(G_OBJECT(entry), "key-press-event", 
7044                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
7045                          headerentry);
7046         g_signal_connect(G_OBJECT(entry), "changed", 
7047                          G_CALLBACK(compose_headerentry_changed_cb), 
7048                          headerentry);
7049         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
7050                          G_CALLBACK(compose_grab_focus_cb), compose);
7051
7052         g_signal_connect(G_OBJECT(button), "clicked",
7053                          G_CALLBACK(compose_headerentry_button_clicked_cb),
7054                          headerentry); 
7055                          
7056         /* email dnd */
7057         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7058                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7059                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7060         g_signal_connect(G_OBJECT(entry), "drag_data_received",
7061                          G_CALLBACK(compose_header_drag_received_cb),
7062                          entry);
7063         g_signal_connect(G_OBJECT(entry), "drag-drop",
7064                          G_CALLBACK(compose_drag_drop),
7065                          compose);
7066         g_signal_connect(G_OBJECT(entry), "populate-popup",
7067                          G_CALLBACK(compose_entry_popup_extend),
7068                          NULL);
7069         
7070         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
7071
7072         headerentry->compose = compose;
7073         headerentry->combo = combo;
7074         headerentry->entry = entry;
7075         headerentry->button = button;
7076         headerentry->hbox = hbox;
7077         headerentry->headernum = compose->header_nextrow;
7078         headerentry->type = PREF_NONE;
7079
7080         compose->header_nextrow++;
7081         compose->header_last = headerentry;             
7082         compose->header_list =
7083                 g_slist_append(compose->header_list,
7084                                headerentry);
7085 }
7086
7087 static void compose_add_header_entry(Compose *compose, const gchar *header,
7088                                 gchar *text, ComposePrefType pref_type) 
7089 {
7090         ComposeHeaderEntry *last_header = compose->header_last;
7091         gchar *tmp = g_strdup(text), *email;
7092         gboolean replyto_hdr;
7093         
7094         replyto_hdr = (!strcasecmp(header,
7095                                 prefs_common_translated_header_name("Reply-To:")) ||
7096                         !strcasecmp(header,
7097                                 prefs_common_translated_header_name("Followup-To:")) ||
7098                         !strcasecmp(header,
7099                                 prefs_common_translated_header_name("In-Reply-To:")));
7100                 
7101         extract_address(tmp);
7102         email = g_utf8_strdown(tmp, -1);
7103         
7104         if (replyto_hdr == FALSE &&
7105             g_hash_table_lookup(compose->email_hashtable, email) != NULL)
7106         {
7107                 debug_print("Ignoring duplicate address - %s %s, pref_type: %d\n",
7108                                 header, text, (gint) pref_type);
7109                 g_free(email);
7110                 g_free(tmp);
7111                 return;
7112         }
7113         
7114         if (!strcasecmp(header, prefs_common_translated_header_name("In-Reply-To:")))
7115                 gtk_entry_set_text(GTK_ENTRY(
7116                         gtk_bin_get_child(GTK_BIN(last_header->combo))), header);
7117         else
7118                 combobox_select_by_text(GTK_COMBO_BOX(last_header->combo), header);
7119         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
7120         last_header->type = pref_type;
7121
7122         if (replyto_hdr == FALSE)
7123                 g_hash_table_insert(compose->email_hashtable, email,
7124                                     GUINT_TO_POINTER(1));
7125         else
7126                 g_free(email);
7127         
7128         g_free(tmp);
7129 }
7130
7131 static void compose_destroy_headerentry(Compose *compose, 
7132                                         ComposeHeaderEntry *headerentry)
7133 {
7134         gchar *text = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
7135         gchar *email;
7136
7137         extract_address(text);
7138         email = g_utf8_strdown(text, -1);
7139         g_hash_table_remove(compose->email_hashtable, email);
7140         g_free(text);
7141         g_free(email);
7142         
7143         gtk_widget_destroy(headerentry->combo);
7144         gtk_widget_destroy(headerentry->entry);
7145         gtk_widget_destroy(headerentry->button);
7146         gtk_widget_destroy(headerentry->hbox);
7147         g_free(headerentry);
7148 }
7149
7150 static void compose_remove_header_entries(Compose *compose) 
7151 {
7152         GSList *list;
7153         for (list = compose->header_list; list; list = list->next)
7154                 compose_destroy_headerentry(compose, (ComposeHeaderEntry *)list->data);
7155
7156         compose->header_last = NULL;
7157         g_slist_free(compose->header_list);
7158         compose->header_list = NULL;
7159         compose->header_nextrow = 1;
7160         compose_create_header_entry(compose);
7161 }
7162
7163 static GtkWidget *compose_create_header(Compose *compose) 
7164 {
7165         GtkWidget *from_optmenu_hbox;
7166         GtkWidget *header_table_main;
7167         GtkWidget *header_scrolledwin;
7168         GtkWidget *header_table;
7169
7170         /* parent with account selection and from header */
7171         header_table_main = gtk_table_new(2, 2, FALSE);
7172         gtk_widget_show(header_table_main);
7173         gtk_container_set_border_width(GTK_CONTAINER(header_table_main), BORDER_WIDTH);
7174
7175         from_optmenu_hbox = compose_account_option_menu_create(compose);
7176         gtk_table_attach(GTK_TABLE(header_table_main), from_optmenu_hbox,
7177                                   0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
7178
7179         /* child with header labels and entries */
7180         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7181         gtk_widget_show(header_scrolledwin);
7182         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
7183
7184         header_table = gtk_table_new(2, 2, FALSE);
7185         gtk_widget_show(header_table);
7186         gtk_container_set_border_width(GTK_CONTAINER(header_table), 0);
7187         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
7188         gtk_container_set_focus_vadjustment(GTK_CONTAINER(header_table),
7189                         gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(header_scrolledwin)));
7190         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(header_scrolledwin))), GTK_SHADOW_NONE);
7191
7192         gtk_table_attach(GTK_TABLE(header_table_main), header_scrolledwin,
7193                                   0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 2);
7194
7195         compose->header_table = header_table;
7196         compose->header_list = NULL;
7197         compose->header_nextrow = 0;
7198
7199         compose_create_header_entry(compose);
7200
7201         compose->table = NULL;
7202
7203         return header_table_main;
7204 }
7205
7206 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
7207 {
7208         Compose *compose = (Compose *)data;
7209         GdkEventButton event;
7210         
7211         event.button = 3;
7212         event.time = gtk_get_current_event_time();
7213
7214         return attach_button_pressed(compose->attach_clist, &event, compose);
7215 }
7216
7217 static GtkWidget *compose_create_attach(Compose *compose)
7218 {
7219         GtkWidget *attach_scrwin;
7220         GtkWidget *attach_clist;
7221
7222         GtkListStore *store;
7223         GtkCellRenderer *renderer;
7224         GtkTreeViewColumn *column;
7225         GtkTreeSelection *selection;
7226
7227         /* attachment list */
7228         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
7229         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
7230                                        GTK_POLICY_AUTOMATIC,
7231                                        GTK_POLICY_AUTOMATIC);
7232         gtk_widget_set_size_request(attach_scrwin, -1, 80);
7233
7234         store = gtk_list_store_new(N_ATTACH_COLS, 
7235                                    G_TYPE_STRING,
7236                                    G_TYPE_STRING,
7237                                    G_TYPE_STRING,
7238                                    G_TYPE_STRING,
7239                                    G_TYPE_POINTER,
7240                                    G_TYPE_AUTO_POINTER,
7241                                    -1);
7242         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
7243                                         (GTK_TREE_MODEL(store)));
7244         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
7245         g_object_unref(store);
7246         
7247         renderer = gtk_cell_renderer_text_new();
7248         column = gtk_tree_view_column_new_with_attributes
7249                         (_("Mime type"), renderer, "text", 
7250                          COL_MIMETYPE, NULL);
7251         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7252         
7253         renderer = gtk_cell_renderer_text_new();
7254         column = gtk_tree_view_column_new_with_attributes
7255                         (_("Size"), renderer, "text", 
7256                          COL_SIZE, NULL);
7257         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
7258         
7259         renderer = gtk_cell_renderer_text_new();
7260         column = gtk_tree_view_column_new_with_attributes
7261                         (_("Name"), renderer, "text", 
7262                          COL_NAME, NULL);
7263         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
7264
7265         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
7266                                      prefs_common.use_stripes_everywhere);
7267         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
7268         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
7269
7270         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
7271                          G_CALLBACK(attach_selected), compose);
7272         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
7273                          G_CALLBACK(attach_button_pressed), compose);
7274         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
7275                          G_CALLBACK(popup_attach_button_pressed), compose);
7276         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
7277                          G_CALLBACK(attach_key_pressed), compose);
7278
7279         /* drag and drop */
7280         gtk_drag_dest_set(attach_clist,
7281                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7282                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7283                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7284         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
7285                          G_CALLBACK(compose_attach_drag_received_cb),
7286                          compose);
7287         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
7288                          G_CALLBACK(compose_drag_drop),
7289                          compose);
7290
7291         compose->attach_scrwin = attach_scrwin;
7292         compose->attach_clist  = attach_clist;
7293
7294         return attach_scrwin;
7295 }
7296
7297 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
7298 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
7299
7300 static GtkWidget *compose_create_others(Compose *compose)
7301 {
7302         GtkWidget *table;
7303         GtkWidget *savemsg_checkbtn;
7304         GtkWidget *savemsg_combo;
7305         GtkWidget *savemsg_select;
7306         
7307         guint rowcount = 0;
7308         gchar *folderidentifier;
7309
7310         /* Table for settings */
7311         table = gtk_table_new(3, 1, FALSE);
7312         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
7313         gtk_widget_show(table);
7314         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
7315         rowcount = 0;
7316
7317         /* Save Message to folder */
7318         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
7319         gtk_widget_show(savemsg_checkbtn);
7320         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7321         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7322                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
7323         }
7324         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
7325                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
7326
7327 #if !GTK_CHECK_VERSION(2, 24, 0)
7328         savemsg_combo = gtk_combo_box_entry_new_text();
7329 #else
7330         savemsg_combo = gtk_combo_box_text_new_with_entry();
7331 #endif
7332         compose->savemsg_checkbtn = savemsg_checkbtn;
7333         compose->savemsg_combo = savemsg_combo;
7334         gtk_widget_show(savemsg_combo);
7335
7336         if (prefs_common.compose_save_to_history)
7337 #if !GTK_CHECK_VERSION(2, 24, 0)
7338                 combobox_set_popdown_strings(GTK_COMBO_BOX(savemsg_combo),
7339                                 prefs_common.compose_save_to_history);
7340 #else
7341                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(savemsg_combo),
7342                                 prefs_common.compose_save_to_history);
7343 #endif
7344         gtk_table_attach(GTK_TABLE(table), savemsg_combo, 1, 2, rowcount, rowcount + 1, GTK_FILL|GTK_EXPAND, GTK_SHRINK, 0, 0);
7345         gtk_widget_set_sensitive(GTK_WIDGET(savemsg_combo), prefs_common.savemsg);
7346         g_signal_connect_after(G_OBJECT(savemsg_combo), "grab_focus",
7347                          G_CALLBACK(compose_grab_focus_cb), compose);
7348         if (account_get_special_folder(compose->account, F_OUTBOX)) {
7349                 folderidentifier = folder_item_get_identifier(account_get_special_folder
7350                                   (compose->account, F_OUTBOX));
7351                 compose_set_save_to(compose, folderidentifier);
7352                 g_free(folderidentifier);
7353         }
7354
7355         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
7356         gtk_widget_show(savemsg_select);
7357         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
7358         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
7359                          G_CALLBACK(compose_savemsg_select_cb),
7360                          compose);
7361
7362         return table;   
7363 }
7364
7365 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
7366 {
7367         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo),
7368                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
7369 }
7370
7371 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
7372 {
7373         FolderItem *dest;
7374         gchar * path;
7375
7376         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
7377         if (!dest) return;
7378
7379         path = folder_item_get_identifier(dest);
7380
7381         compose_set_save_to(compose, path);
7382         g_free(path);
7383 }
7384
7385 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
7386                                   GdkAtom clip, GtkTextIter *insert_place);
7387
7388
7389 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
7390                                        Compose *compose)
7391 {
7392         gint prev_autowrap;
7393         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7394 #if USE_ENCHANT
7395         if (event->button == 3) {
7396                 GtkTextIter iter;
7397                 GtkTextIter sel_start, sel_end;
7398                 gboolean stuff_selected;
7399                 gint x, y;
7400                 /* move the cursor to allow GtkAspell to check the word
7401                  * under the mouse */
7402                 if (event->x && event->y) {
7403                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7404                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7405                                 &x, &y);
7406                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7407                                 &iter, x, y);
7408                 } else {
7409                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
7410                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7411                 }
7412                 /* get selection */
7413                 stuff_selected = gtk_text_buffer_get_selection_bounds(
7414                                 buffer,
7415                                 &sel_start, &sel_end);
7416
7417                 gtk_text_buffer_place_cursor (buffer, &iter);
7418                 /* reselect stuff */
7419                 if (stuff_selected 
7420                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
7421                         gtk_text_buffer_select_range(buffer,
7422                                 &sel_start, &sel_end);
7423                 }
7424                 return FALSE; /* pass the event so that the right-click goes through */
7425         }
7426 #endif
7427         if (event->button == 2) {
7428                 GtkTextIter iter;
7429                 gint x, y;
7430                 BLOCK_WRAP();
7431                 
7432                 /* get the middle-click position to paste at the correct place */
7433                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
7434                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
7435                         &x, &y);
7436                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
7437                         &iter, x, y);
7438                 
7439                 entry_paste_clipboard(compose, text, 
7440                                 prefs_common.linewrap_pastes,
7441                                 GDK_SELECTION_PRIMARY, &iter);
7442                 UNBLOCK_WRAP();
7443                 return TRUE;
7444         }
7445         return FALSE;
7446 }
7447
7448 #if USE_ENCHANT
7449 static void compose_spell_menu_changed(void *data)
7450 {
7451         Compose *compose = (Compose *)data;
7452         GSList *items;
7453         GtkWidget *menuitem;
7454         GtkWidget *parent_item;
7455         GtkMenu *menu = GTK_MENU(gtk_menu_new());
7456         GSList *spell_menu;
7457
7458         if (compose->gtkaspell == NULL)
7459                 return;
7460
7461         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
7462                         "/Menu/Spelling/Options");
7463
7464         /* setting the submenu removes /Spelling/Options from the factory 
7465          * so we need to save it */
7466
7467         if (parent_item == NULL) {
7468                 parent_item = compose->aspell_options_menu;
7469                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
7470         } else
7471                 compose->aspell_options_menu = parent_item;
7472
7473         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
7474
7475         spell_menu = g_slist_reverse(spell_menu);
7476         for (items = spell_menu;
7477              items; items = items->next) {
7478                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
7479                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
7480                 gtk_widget_show(GTK_WIDGET(menuitem));
7481         }
7482         g_slist_free(spell_menu);
7483
7484         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
7485         gtk_widget_show(parent_item);
7486 }
7487
7488 static void compose_dict_changed(void *data)
7489 {
7490         Compose *compose = (Compose *) data;
7491
7492         if(!compose->gtkaspell)
7493                 return; 
7494         if(compose->gtkaspell->recheck_when_changing_dict == FALSE)
7495                 return;
7496
7497         gtkaspell_highlight_all(compose->gtkaspell);
7498         claws_spell_entry_recheck_all(CLAWS_SPELL_ENTRY(compose->subject_entry));
7499 }
7500 #endif
7501
7502 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
7503 {
7504         Compose *compose = (Compose *)data;
7505         GdkEventButton event;
7506         
7507         event.button = 3;
7508         event.time = gtk_get_current_event_time();
7509         event.x = 0;
7510         event.y = 0;
7511
7512         return text_clicked(compose->text, &event, compose);
7513 }
7514
7515 static gboolean compose_force_window_origin = TRUE;
7516 static Compose *compose_create(PrefsAccount *account,
7517                                                  FolderItem *folder,
7518                                                  ComposeMode mode,
7519                                                  gboolean batch)
7520 {
7521         Compose   *compose;
7522         GtkWidget *window;
7523         GtkWidget *vbox;
7524         GtkWidget *menubar;
7525         GtkWidget *handlebox;
7526
7527         GtkWidget *notebook;
7528         
7529         GtkWidget *attach_hbox;
7530         GtkWidget *attach_lab1;
7531         GtkWidget *attach_lab2;
7532
7533         GtkWidget *vbox2;
7534
7535         GtkWidget *label;
7536         GtkWidget *subject_hbox;
7537         GtkWidget *subject_frame;
7538         GtkWidget *subject_entry;
7539         GtkWidget *subject;
7540         GtkWidget *paned;
7541
7542         GtkWidget *edit_vbox;
7543         GtkWidget *ruler_hbox;
7544         GtkWidget *ruler;
7545         GtkWidget *scrolledwin;
7546         GtkWidget *text;
7547         GtkTextBuffer *buffer;
7548         GtkClipboard *clipboard;
7549
7550         UndoMain *undostruct;
7551
7552         GtkWidget *popupmenu;
7553         GtkWidget *tmpl_menu;
7554         GtkActionGroup *action_group = NULL;
7555
7556 #if USE_ENCHANT
7557         GtkAspell * gtkaspell = NULL;
7558 #endif
7559
7560         static GdkGeometry geometry;
7561
7562         cm_return_val_if_fail(account != NULL, NULL);
7563
7564         gtkut_convert_int_to_gdk_color(prefs_common.default_header_bgcolor,
7565                                            &default_header_bgcolor);
7566         gtkut_convert_int_to_gdk_color(prefs_common.default_header_color,
7567                                            &default_header_color);
7568
7569         debug_print("Creating compose window...\n");
7570         compose = g_new0(Compose, 1);
7571
7572         compose->batch = batch;
7573         compose->account = account;
7574         compose->folder = folder;
7575         
7576         compose->mutex = cm_mutex_new();
7577         compose->set_cursor_pos = -1;
7578
7579         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
7580
7581         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
7582         gtk_widget_set_size_request(window, prefs_common.compose_width,
7583                                         prefs_common.compose_height);
7584
7585         if (!geometry.max_width) {
7586                 geometry.max_width = gdk_screen_width();
7587                 geometry.max_height = gdk_screen_height();
7588         }
7589
7590         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7591                                       &geometry, GDK_HINT_MAX_SIZE);
7592         if (!geometry.min_width) {
7593                 geometry.min_width = 600;
7594                 geometry.min_height = 440;
7595         }
7596         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
7597                                       &geometry, GDK_HINT_MIN_SIZE);
7598
7599 #ifndef GENERIC_UMPC    
7600         if (compose_force_window_origin)
7601                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
7602                                  prefs_common.compose_y);
7603 #endif
7604         g_signal_connect(G_OBJECT(window), "delete_event",
7605                          G_CALLBACK(compose_delete_cb), compose);
7606         MANAGE_WINDOW_SIGNALS_CONNECT(window);
7607         gtk_widget_realize(window);
7608
7609         gtkut_widget_set_composer_icon(window);
7610
7611         vbox = gtk_vbox_new(FALSE, 0);
7612         gtk_container_add(GTK_CONTAINER(window), vbox);
7613
7614         compose->ui_manager = gtk_ui_manager_new();
7615         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
7616                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
7617         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
7618                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
7619         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
7620                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
7621         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
7622                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
7623         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
7624                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
7625
7626         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
7627
7628         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
7629         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
7630 #ifdef USE_ENCHANT
7631         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
7632 #endif
7633         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
7634         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
7635         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
7636
7637 /* Compose menu */
7638         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
7639         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
7640         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7641         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
7642         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
7643         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
7644         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "ReplaceSig", "Message/ReplaceSig", GTK_UI_MANAGER_MENUITEM)
7645         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7646         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
7647         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7648         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Print", "Message/Print", GTK_UI_MANAGER_MENUITEM)
7649         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
7650         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
7651
7652 /* Edit menu */
7653         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
7654         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
7655         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7656
7657         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
7658         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
7659         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
7660
7661         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
7662         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
7663         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
7664         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
7665
7666         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
7667
7668         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
7669         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
7670         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
7671         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
7672         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
7673         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
7674         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
7675         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
7676         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
7677         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
7678         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
7679         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
7680         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
7681         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
7682         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
7683
7684         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7685
7686         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
7687         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
7688         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
7689         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
7690         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoIndent", "Edit/AutoIndent", GTK_UI_MANAGER_MENUITEM)
7691
7692         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
7693
7694         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
7695
7696 #if USE_ENCHANT
7697 /* Spelling menu */
7698         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
7699         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
7700         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
7701         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
7702         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
7703         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
7704 #endif
7705
7706 /* Options menu */
7707         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
7708         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
7709         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
7710         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
7711         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
7712
7713         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7714         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
7715         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7716         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
7717         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
7718
7719         
7720         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7721         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
7722         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
7723         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
7724         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
7725         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
7726         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
7727
7728         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7729         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
7730         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7731         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
7732         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
7733
7734         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
7735
7736         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
7737         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7738         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
7739         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
7740         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
7741
7742         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
7743         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)
7744         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)
7745         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
7746
7747         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
7748
7749         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
7750         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)
7751         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)
7752
7753         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
7754
7755         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
7756         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)
7757         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
7758
7759         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
7760         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)
7761         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
7762
7763         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
7764
7765         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
7766         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)
7767         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
7768         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_MACCYR, "Options/Encoding/Cyrillic/"CS_MACCYR, GTK_UI_MANAGER_MENUITEM)
7769         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
7770         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
7771
7772         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
7773         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)
7774         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)
7775         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
7776         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
7777
7778         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
7779         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB18030, "Options/Encoding/Chinese/"CS_GB18030, GTK_UI_MANAGER_MENUITEM)
7780         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
7781         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
7782         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
7783         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
7784
7785         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
7786         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
7787         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)
7788
7789         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
7790         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
7791         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
7792 /* phew. */
7793
7794 /* Tools menu */
7795         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
7796         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
7797         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
7798         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7799         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
7800         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
7801
7802 /* Help menu */
7803         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
7804
7805         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
7806         gtk_widget_show_all(menubar);
7807
7808         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
7809         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
7810
7811         if (prefs_common.toolbar_detachable) {
7812                 handlebox = gtk_handle_box_new();
7813         } else {
7814                 handlebox = gtk_hbox_new(FALSE, 0);
7815         }
7816         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
7817
7818         gtk_widget_realize(handlebox);
7819         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
7820                                           (gpointer)compose);
7821
7822         vbox2 = gtk_vbox_new(FALSE, 2);
7823         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
7824         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
7825         
7826         /* Notebook */
7827         notebook = gtk_notebook_new();
7828         gtk_widget_show(notebook);
7829
7830         /* header labels and entries */
7831         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7832                         compose_create_header(compose),
7833                         gtk_label_new_with_mnemonic(_("Hea_der")));
7834         /* attachment list */
7835         attach_hbox = gtk_hbox_new(FALSE, 0);
7836         gtk_widget_show(attach_hbox);
7837         
7838         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7839         gtk_widget_show(attach_lab1);
7840         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7841         
7842         attach_lab2 = gtk_label_new("");
7843         gtk_widget_show(attach_lab2);
7844         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7845         
7846         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7847                         compose_create_attach(compose),
7848                         attach_hbox);
7849         /* Others Tab */
7850         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7851                         compose_create_others(compose),
7852                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7853
7854         /* Subject */
7855         subject_hbox = gtk_hbox_new(FALSE, 0);
7856         gtk_widget_show(subject_hbox);
7857
7858         subject_frame = gtk_frame_new(NULL);
7859         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7860         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7861         gtk_widget_show(subject_frame);
7862
7863         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7864         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7865         gtk_widget_show(subject);
7866
7867         label = gtk_label_new_with_mnemonic(_("S_ubject:"));
7868         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7869         gtk_widget_show(label);
7870
7871 #ifdef USE_ENCHANT
7872         subject_entry = claws_spell_entry_new();
7873 #else
7874         subject_entry = gtk_entry_new();
7875 #endif
7876         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7877         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7878                          G_CALLBACK(compose_grab_focus_cb), compose);
7879         gtk_label_set_mnemonic_widget(GTK_LABEL(label), subject_entry);
7880         gtk_widget_show(subject_entry);
7881         compose->subject_entry = subject_entry;
7882         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7883         
7884         edit_vbox = gtk_vbox_new(FALSE, 0);
7885
7886         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7887
7888         /* ruler */
7889         ruler_hbox = gtk_hbox_new(FALSE, 0);
7890         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7891
7892         ruler = gtk_shruler_new(GTK_ORIENTATION_HORIZONTAL);
7893         gtk_shruler_set_range(GTK_SHRULER(ruler), 0.0, 100.0, 1.0);
7894         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7895                            BORDER_WIDTH);
7896
7897         /* text widget */
7898         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7899         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7900                                        GTK_POLICY_AUTOMATIC,
7901                                        GTK_POLICY_AUTOMATIC);
7902         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7903                                             GTK_SHADOW_IN);
7904         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7905
7906         text = gtk_text_view_new();
7907         if (prefs_common.show_compose_margin) {
7908                 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
7909                 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
7910         }
7911         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7912         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7913         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7914         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7915         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7916         
7917         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7918         g_signal_connect_after(G_OBJECT(text), "size_allocate",
7919                                G_CALLBACK(compose_edit_size_alloc),
7920                                ruler);
7921         g_signal_connect(G_OBJECT(buffer), "changed",
7922                          G_CALLBACK(compose_changed_cb), compose);
7923         g_signal_connect(G_OBJECT(text), "grab_focus",
7924                          G_CALLBACK(compose_grab_focus_cb), compose);
7925         g_signal_connect(G_OBJECT(buffer), "insert_text",
7926                          G_CALLBACK(text_inserted), compose);
7927         g_signal_connect(G_OBJECT(text), "button_press_event",
7928                          G_CALLBACK(text_clicked), compose);
7929         g_signal_connect(G_OBJECT(text), "popup-menu",
7930                          G_CALLBACK(compose_popup_menu), compose);
7931         g_signal_connect(G_OBJECT(subject_entry), "changed",
7932                         G_CALLBACK(compose_changed_cb), compose);
7933         g_signal_connect(G_OBJECT(subject_entry), "activate",
7934                         G_CALLBACK(compose_subject_entry_activated), compose);
7935
7936         /* drag and drop */
7937         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7938                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7939                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7940         g_signal_connect(G_OBJECT(text), "drag_data_received",
7941                          G_CALLBACK(compose_insert_drag_received_cb),
7942                          compose);
7943         g_signal_connect(G_OBJECT(text), "drag-drop",
7944                          G_CALLBACK(compose_drag_drop),
7945                          compose);
7946         g_signal_connect(G_OBJECT(text), "key-press-event",
7947                          G_CALLBACK(completion_set_focus_to_subject),
7948                          compose);
7949         gtk_widget_show_all(vbox);
7950
7951         /* pane between attach clist and text */
7952         paned = gtk_vpaned_new();
7953         gtk_container_add(GTK_CONTAINER(vbox2), paned);
7954         gtk_paned_pack1(GTK_PANED(paned), notebook, FALSE, FALSE);
7955         gtk_paned_pack2(GTK_PANED(paned), edit_vbox, TRUE, FALSE);
7956         gtk_paned_set_position(GTK_PANED(paned), prefs_common.compose_notebook_height);
7957         g_signal_connect(G_OBJECT(notebook), "size_allocate",
7958                          G_CALLBACK(compose_notebook_size_alloc), paned);
7959
7960         gtk_widget_show_all(paned);
7961
7962
7963         if (prefs_common.textfont) {
7964                 PangoFontDescription *font_desc;
7965
7966                 font_desc = pango_font_description_from_string
7967                         (prefs_common.textfont);
7968                 if (font_desc) {
7969                         gtk_widget_modify_font(text, font_desc);
7970                         pango_font_description_free(font_desc);
7971                 }
7972         }
7973
7974         gtk_action_group_add_actions(action_group, compose_popup_entries,
7975                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7976         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7977         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7978         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7979         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7980         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7981         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7982         
7983         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7984
7985         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7986         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7987         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7988
7989         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7990
7991         undostruct = undo_init(text);
7992         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7993                                    compose);
7994
7995         address_completion_start(window);
7996
7997         compose->window        = window;
7998         compose->vbox          = vbox;
7999         compose->menubar       = menubar;
8000         compose->handlebox     = handlebox;
8001
8002         compose->vbox2         = vbox2;
8003
8004         compose->paned = paned;
8005
8006         compose->attach_label  = attach_lab2;
8007
8008         compose->notebook      = notebook;
8009         compose->edit_vbox     = edit_vbox;
8010         compose->ruler_hbox    = ruler_hbox;
8011         compose->ruler         = ruler;
8012         compose->scrolledwin   = scrolledwin;
8013         compose->text          = text;
8014
8015         compose->focused_editable = NULL;
8016
8017         compose->popupmenu    = popupmenu;
8018
8019         compose->tmpl_menu = tmpl_menu;
8020
8021         compose->mode = mode;
8022         compose->rmode = mode;
8023
8024         compose->targetinfo = NULL;
8025         compose->replyinfo  = NULL;
8026         compose->fwdinfo    = NULL;
8027
8028         compose->email_hashtable = g_hash_table_new_full(g_str_hash,
8029                                 g_str_equal, (GDestroyNotify) g_free, NULL);
8030         
8031         compose->replyto     = NULL;
8032         compose->cc          = NULL;
8033         compose->bcc         = NULL;
8034         compose->followup_to = NULL;
8035
8036         compose->ml_post     = NULL;
8037
8038         compose->inreplyto   = NULL;
8039         compose->references  = NULL;
8040         compose->msgid       = NULL;
8041         compose->boundary    = NULL;
8042
8043         compose->autowrap       = prefs_common.autowrap;
8044         compose->autoindent     = prefs_common.auto_indent;
8045         compose->use_signing    = FALSE;
8046         compose->use_encryption = FALSE;
8047         compose->privacy_system = NULL;
8048         compose->encdata        = NULL;
8049
8050         compose->modified = FALSE;
8051
8052         compose->return_receipt = FALSE;
8053
8054         compose->to_list        = NULL;
8055         compose->newsgroup_list = NULL;
8056
8057         compose->undostruct = undostruct;
8058
8059         compose->sig_str = NULL;
8060
8061         compose->exteditor_file    = NULL;
8062         compose->exteditor_pid     = -1;
8063         compose->exteditor_tag     = -1;
8064         compose->exteditor_socket  = NULL;
8065         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; /* inhibit auto-drafting while loading */
8066
8067         compose->folder_update_callback_id =
8068                 hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
8069                                 compose_update_folder_hook,
8070                                 (gpointer) compose);
8071
8072 #if USE_ENCHANT
8073         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
8074         if (mode != COMPOSE_REDIRECT) {
8075                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
8076                     strcmp(prefs_common.dictionary, "")) {
8077                         gtkaspell = gtkaspell_new(prefs_common.dictionary,
8078                                                   prefs_common.alt_dictionary,
8079                                                   conv_get_locale_charset_str(),
8080                                                   prefs_common.misspelled_col,
8081                                                   prefs_common.check_while_typing,
8082                                                   prefs_common.recheck_when_changing_dict,
8083                                                   prefs_common.use_alternate,
8084                                                   prefs_common.use_both_dicts,
8085                                                   GTK_TEXT_VIEW(text),
8086                                                   GTK_WINDOW(compose->window),
8087                                                   compose_dict_changed,
8088                                                   compose_spell_menu_changed,
8089                                                   compose);
8090                         if (!gtkaspell) {
8091                                 alertpanel_error(_("Spell checker could not "
8092                                                 "be started.\n%s"),
8093                                                 gtkaspell_checkers_strerror());
8094                                 gtkaspell_checkers_reset_error();
8095                         } else {
8096                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
8097                         }
8098                 }
8099         }
8100         compose->gtkaspell = gtkaspell;
8101         compose_spell_menu_changed(compose);
8102         claws_spell_entry_set_gtkaspell(CLAWS_SPELL_ENTRY(subject_entry), gtkaspell);
8103 #endif
8104
8105         compose_select_account(compose, account, TRUE);
8106
8107         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
8108         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", prefs_common.auto_indent);
8109
8110         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
8111                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8112
8113         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
8114                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8115         
8116         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
8117                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8118
8119         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
8120         if (account->protocol != A_NNTP)
8121                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8122                                 prefs_common_translated_header_name("To:"));
8123         else
8124                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
8125                                 prefs_common_translated_header_name("Newsgroups:"));
8126
8127 #ifndef USE_ALT_ADDRBOOK
8128         addressbook_set_target_compose(compose);
8129 #endif  
8130         if (mode != COMPOSE_REDIRECT)
8131                 compose_set_template_menu(compose);
8132         else {
8133                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
8134         }
8135
8136         compose_list = g_list_append(compose_list, compose);
8137
8138         if (!prefs_common.show_ruler)
8139                 gtk_widget_hide(ruler_hbox);
8140                 
8141         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
8142
8143         /* Priority */
8144         compose->priority = PRIORITY_NORMAL;
8145         compose_update_priority_menu_item(compose);
8146
8147         compose_set_out_encoding(compose);
8148         
8149         /* Actions menu */
8150         compose_update_actions_menu(compose);
8151
8152         /* Privacy Systems menu */
8153         compose_update_privacy_systems_menu(compose);
8154
8155         activate_privacy_system(compose, account, TRUE);
8156         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
8157         if (batch) {
8158                 gtk_widget_realize(window);
8159         } else {
8160                 gtk_widget_show(window);
8161         }
8162         
8163         return compose;
8164 }
8165
8166 static GtkWidget *compose_account_option_menu_create(Compose *compose)
8167 {
8168         GList *accounts;
8169         GtkWidget *hbox;
8170         GtkWidget *optmenu;
8171         GtkWidget *optmenubox;
8172         GtkWidget *fromlabel;
8173         GtkListStore *menu;
8174         GtkTreeIter iter;
8175         GtkWidget *from_name = NULL;
8176
8177         gint num = 0, def_menu = 0;
8178         
8179         accounts = account_get_list();
8180         cm_return_val_if_fail(accounts != NULL, NULL);
8181
8182         optmenubox = gtk_event_box_new();
8183         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
8184         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8185
8186         hbox = gtk_hbox_new(FALSE, 4);
8187         from_name = gtk_entry_new();
8188         
8189         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
8190                          G_CALLBACK(compose_grab_focus_cb), compose);
8191         g_signal_connect_after(G_OBJECT(from_name), "activate",
8192                          G_CALLBACK(from_name_activate_cb), optmenu);
8193
8194         for (; accounts != NULL; accounts = accounts->next, num++) {
8195                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
8196                 gchar *name, *from = NULL;
8197
8198                 if (ac == compose->account) def_menu = num;
8199
8200                 name = g_markup_printf_escaped("<i>%s</i>",
8201                                        ac->account_name);
8202                 
8203                 if (ac == compose->account) {
8204                         if (ac->name && *ac->name) {
8205                                 gchar *buf;
8206                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
8207                                 from = g_strdup_printf("%s <%s>",
8208                                                        buf, ac->address);
8209                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8210                         } else {
8211                                 from = g_strdup_printf("%s",
8212                                                        ac->address);
8213                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
8214                         }
8215                         if (cur_account != compose->account) {
8216                                 gtk_widget_modify_base(
8217                                         GTK_WIDGET(from_name),
8218                                         GTK_STATE_NORMAL, &default_header_bgcolor);
8219                                 gtk_widget_modify_text(
8220                                         GTK_WIDGET(from_name),
8221                                         GTK_STATE_NORMAL, &default_header_color);
8222                         }
8223                 }
8224                 COMBOBOX_ADD(menu, name, ac->account_id);
8225                 g_free(name);
8226                 g_free(from);
8227         }
8228
8229         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
8230
8231         g_signal_connect(G_OBJECT(optmenu), "changed",
8232                         G_CALLBACK(account_activated),
8233                         compose);
8234         g_signal_connect(G_OBJECT(from_name), "populate-popup",
8235                          G_CALLBACK(compose_entry_popup_extend),
8236                          NULL);
8237
8238         fromlabel = gtk_label_new_with_mnemonic(_("_From:"));
8239         gtk_label_set_mnemonic_widget(GTK_LABEL(fromlabel), from_name);
8240
8241         gtk_box_pack_start(GTK_BOX(hbox), fromlabel, FALSE, FALSE, 4);
8242         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
8243         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
8244
8245         /* Putting only the GtkEntry into focus chain of parent hbox causes
8246          * the account selector combobox next to it to be unreachable when
8247          * navigating widgets in GtkTable with up/down arrow keys.
8248          * Note: gtk_widget_set_can_focus() was not enough. */
8249         GList *l = NULL;
8250         l = g_list_prepend(l, from_name);
8251         gtk_container_set_focus_chain(GTK_CONTAINER(hbox), l);
8252         g_list_free(l);
8253         
8254         CLAWS_SET_TIP(optmenubox,
8255                 _("Account to use for this email"));
8256         CLAWS_SET_TIP(from_name,
8257                 _("Sender address to be used"));
8258
8259         compose->account_combo = optmenu;
8260         compose->from_name = from_name;
8261         
8262         return hbox;
8263 }
8264
8265 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8266 {
8267         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8268         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8269         Compose *compose = (Compose *) data;
8270         if (active) {
8271                 compose->priority = value;
8272         }
8273 }
8274
8275 static void compose_reply_change_mode(Compose *compose,
8276                                     ComposeMode action)
8277 {
8278         gboolean was_modified = compose->modified;
8279
8280         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
8281         
8282         cm_return_if_fail(compose->replyinfo != NULL);
8283         
8284         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
8285                 ml = TRUE;
8286         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
8287                 followup = TRUE;
8288         if (action == COMPOSE_REPLY_TO_ALL)
8289                 all = TRUE;
8290         if (action == COMPOSE_REPLY_TO_SENDER)
8291                 sender = TRUE;
8292         if (action == COMPOSE_REPLY_TO_LIST)
8293                 ml = TRUE;
8294
8295         compose_remove_header_entries(compose);
8296         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
8297         if (compose->account->set_autocc && compose->account->auto_cc)
8298                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC, PREF_ACCOUNT);
8299
8300         if (compose->account->set_autobcc && compose->account->auto_bcc) 
8301                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC, PREF_ACCOUNT);
8302         
8303         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
8304                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO, PREF_ACCOUNT);
8305         compose_show_first_last_header(compose, TRUE);
8306         compose->modified = was_modified;
8307         compose_set_title(compose);
8308 }
8309
8310 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
8311 {
8312         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
8313         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
8314         Compose *compose = (Compose *) data;
8315         
8316         if (active)
8317                 compose_reply_change_mode(compose, value);
8318 }
8319
8320 static void compose_update_priority_menu_item(Compose * compose)
8321 {
8322         GtkWidget *menuitem = NULL;
8323         switch (compose->priority) {
8324                 case PRIORITY_HIGHEST:
8325                         menuitem = gtk_ui_manager_get_widget
8326                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
8327                         break;
8328                 case PRIORITY_HIGH:
8329                         menuitem = gtk_ui_manager_get_widget
8330                                 (compose->ui_manager, "/Menu/Options/Priority/High");
8331                         break;
8332                 case PRIORITY_NORMAL:
8333                         menuitem = gtk_ui_manager_get_widget
8334                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
8335                         break;
8336                 case PRIORITY_LOW:
8337                         menuitem = gtk_ui_manager_get_widget
8338                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
8339                         break;
8340                 case PRIORITY_LOWEST:
8341                         menuitem = gtk_ui_manager_get_widget
8342                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
8343                         break;
8344         }
8345         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8346 }       
8347
8348 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
8349 {
8350         Compose *compose = (Compose *) data;
8351         gchar *systemid;
8352         gboolean can_sign = FALSE, can_encrypt = FALSE;
8353
8354         cm_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
8355
8356         if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8357                 return;
8358
8359         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
8360         g_free(compose->privacy_system);
8361         compose->privacy_system = NULL;
8362         g_free(compose->encdata);
8363         compose->encdata = NULL;
8364         if (systemid != NULL) {
8365                 compose->privacy_system = g_strdup(systemid);
8366
8367                 can_sign = privacy_system_can_sign(systemid);
8368                 can_encrypt = privacy_system_can_encrypt(systemid);
8369         }
8370
8371         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
8372
8373         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8374         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8375 }
8376
8377 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
8378 {
8379         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8380         GtkWidget *menuitem = NULL;
8381         GList *children, *amenu;
8382         gboolean can_sign = FALSE, can_encrypt = FALSE;
8383         gboolean found = FALSE;
8384
8385         if (compose->privacy_system != NULL) {
8386                 gchar *systemid;
8387                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
8388                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
8389                 cm_return_if_fail(menuitem != NULL);
8390
8391                 children = gtk_container_get_children(GTK_CONTAINER(GTK_MENU_SHELL(menuitem)));
8392                 amenu = children;
8393                 menuitem = NULL;
8394                 while (amenu != NULL) {
8395                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
8396                         if (systemid != NULL) {
8397                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
8398                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8399                                         menuitem = GTK_WIDGET(amenu->data);
8400
8401                                         can_sign = privacy_system_can_sign(systemid);
8402                                         can_encrypt = privacy_system_can_encrypt(systemid);
8403                                         found = TRUE;
8404                                         break;
8405                                 } 
8406                         } else if (strlen(compose->privacy_system) == 0 && 
8407                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
8408                                         menuitem = GTK_WIDGET(amenu->data);
8409
8410                                         can_sign = FALSE;
8411                                         can_encrypt = FALSE;
8412                                         found = TRUE;
8413                                         break;
8414                         }
8415
8416                         amenu = amenu->next;
8417                 }
8418                 g_list_free(children);
8419                 if (menuitem != NULL)
8420                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
8421                 
8422                 if (warn && !found && strlen(compose->privacy_system)) {
8423                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
8424                                   "will not be able to sign or encrypt this message."),
8425                                   compose->privacy_system);
8426                 }
8427         } 
8428
8429         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
8430         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
8431 }       
8432  
8433 static void compose_set_out_encoding(Compose *compose)
8434 {
8435         CharSet out_encoding;
8436         const gchar *branch = NULL;
8437         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
8438
8439         switch(out_encoding) {
8440                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8441                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
8442                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
8443                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
8444                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
8445                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
8446                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
8447                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
8448                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
8449                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
8450                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
8451                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
8452                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
8453                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
8454                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
8455                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
8456                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
8457                 case C_MACCYR: branch = "Menu/Options/Encoding/Cyrillic/" CS_MACCYR; break;
8458                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
8459                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
8460                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
8461                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
8462                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
8463                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
8464                 case C_GB18030: branch = "Menu/Options/Encoding/Chinese/" CS_GB18030; break;
8465                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
8466                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
8467                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
8468                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
8469                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
8470                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
8471                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
8472                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
8473                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
8474         }
8475         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
8476 }
8477
8478 static void compose_set_template_menu(Compose *compose)
8479 {
8480         GSList *tmpl_list, *cur;
8481         GtkWidget *menu;
8482         GtkWidget *item;
8483
8484         tmpl_list = template_get_config();
8485
8486         menu = gtk_menu_new();
8487
8488         gtk_menu_set_accel_group (GTK_MENU (menu), 
8489                 gtk_ui_manager_get_accel_group(compose->ui_manager));
8490         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
8491                 Template *tmpl = (Template *)cur->data;
8492                 gchar *accel_path = NULL;
8493                 item = gtk_menu_item_new_with_label(tmpl->name);
8494                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
8495                 g_signal_connect(G_OBJECT(item), "activate",
8496                                  G_CALLBACK(compose_template_activate_cb),
8497                                  compose);
8498                 g_object_set_data(G_OBJECT(item), "template", tmpl);
8499                 gtk_widget_show(item);
8500                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
8501                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
8502                 g_free(accel_path);
8503         }
8504
8505         gtk_widget_show(menu);
8506         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
8507 }
8508
8509 void compose_update_actions_menu(Compose *compose)
8510 {
8511         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
8512 }
8513
8514 static void compose_update_privacy_systems_menu(Compose *compose)
8515 {
8516         static gchar *branch_path = "/Menu/Options/PrivacySystem";
8517         GSList *systems, *cur;
8518         GtkWidget *widget;
8519         GtkWidget *system_none;
8520         GSList *group;
8521         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
8522         GtkWidget *privacy_menu = gtk_menu_new();
8523
8524         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
8525         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
8526
8527         g_signal_connect(G_OBJECT(system_none), "activate",
8528                 G_CALLBACK(compose_set_privacy_system_cb), compose);
8529
8530         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
8531         gtk_widget_show(system_none);
8532
8533         systems = privacy_get_system_ids();
8534         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
8535                 gchar *systemid = cur->data;
8536
8537                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
8538                 widget = gtk_radio_menu_item_new_with_label(group,
8539                         privacy_system_get_name(systemid));
8540                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
8541                                        g_strdup(systemid), g_free);
8542                 g_signal_connect(G_OBJECT(widget), "activate",
8543                         G_CALLBACK(compose_set_privacy_system_cb), compose);
8544
8545                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
8546                 gtk_widget_show(widget);
8547                 g_free(systemid);
8548         }
8549         g_slist_free(systems);
8550         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
8551         gtk_widget_show_all(privacy_menu);
8552         gtk_widget_show_all(privacy_menuitem);
8553 }
8554
8555 void compose_reflect_prefs_all(void)
8556 {
8557         GList *cur;
8558         Compose *compose;
8559
8560         for (cur = compose_list; cur != NULL; cur = cur->next) {
8561                 compose = (Compose *)cur->data;
8562                 compose_set_template_menu(compose);
8563         }
8564 }
8565
8566 void compose_reflect_prefs_pixmap_theme(void)
8567 {
8568         GList *cur;
8569         Compose *compose;
8570
8571         for (cur = compose_list; cur != NULL; cur = cur->next) {
8572                 compose = (Compose *)cur->data;
8573                 toolbar_update(TOOLBAR_COMPOSE, compose);
8574         }
8575 }
8576
8577 static const gchar *compose_quote_char_from_context(Compose *compose)
8578 {
8579         const gchar *qmark = NULL;
8580
8581         cm_return_val_if_fail(compose != NULL, NULL);
8582
8583         switch (compose->mode) {
8584                 /* use forward-specific quote char */
8585                 case COMPOSE_FORWARD:
8586                 case COMPOSE_FORWARD_AS_ATTACH:
8587                 case COMPOSE_FORWARD_INLINE:
8588                         if (compose->folder && compose->folder->prefs &&
8589                                         compose->folder->prefs->forward_with_format)
8590                                 qmark = compose->folder->prefs->forward_quotemark;
8591                         else if (compose->account->forward_with_format)
8592                                 qmark = compose->account->forward_quotemark;
8593                         else
8594                                 qmark = prefs_common.fw_quotemark;
8595                         break;
8596
8597                 /* use reply-specific quote char in all other modes */
8598                 default:
8599                         if (compose->folder && compose->folder->prefs &&
8600                                         compose->folder->prefs->reply_with_format)
8601                                 qmark = compose->folder->prefs->reply_quotemark;
8602                         else if (compose->account->reply_with_format)
8603                                 qmark = compose->account->reply_quotemark;
8604                         else
8605                                 qmark = prefs_common.quotemark;
8606                         break;
8607         }
8608
8609         if (qmark == NULL || *qmark == '\0')
8610                 qmark = "> ";
8611
8612         return qmark;
8613 }
8614
8615 static void compose_template_apply(Compose *compose, Template *tmpl,
8616                                    gboolean replace)
8617 {
8618         GtkTextView *text;
8619         GtkTextBuffer *buffer;
8620         GtkTextMark *mark;
8621         GtkTextIter iter;
8622         const gchar *qmark;
8623         gchar *parsed_str = NULL;
8624         gint cursor_pos = 0;
8625         const gchar *err_msg = _("The body of the template has an error at line %d.");
8626         if (!tmpl) return;
8627
8628         /* process the body */
8629
8630         text = GTK_TEXT_VIEW(compose->text);
8631         buffer = gtk_text_view_get_buffer(text);
8632
8633         if (tmpl->value) {
8634                 qmark = compose_quote_char_from_context(compose);
8635
8636                 if (compose->replyinfo != NULL) {
8637
8638                         if (replace)
8639                                 gtk_text_buffer_set_text(buffer, "", -1);
8640                         mark = gtk_text_buffer_get_insert(buffer);
8641                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8642
8643                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
8644                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8645
8646                 } else if (compose->fwdinfo != NULL) {
8647
8648                         if (replace)
8649                                 gtk_text_buffer_set_text(buffer, "", -1);
8650                         mark = gtk_text_buffer_get_insert(buffer);
8651                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8652
8653                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
8654                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
8655
8656                 } else {
8657                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
8658
8659                         GtkTextIter start, end;
8660                         gchar *tmp = NULL;
8661
8662                         gtk_text_buffer_get_start_iter(buffer, &start);
8663                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
8664                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
8665
8666                         /* clear the buffer now */
8667                         if (replace)
8668                                 gtk_text_buffer_set_text(buffer, "", -1);
8669
8670                         parsed_str = compose_quote_fmt(compose, dummyinfo,
8671                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
8672                         procmsg_msginfo_free( &dummyinfo );
8673
8674                         g_free( tmp );
8675                 } 
8676         } else {
8677                 if (replace)
8678                         gtk_text_buffer_set_text(buffer, "", -1);
8679                 mark = gtk_text_buffer_get_insert(buffer);
8680                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
8681         }       
8682
8683         if (replace && parsed_str && compose->account->auto_sig)
8684                 compose_insert_sig(compose, FALSE);
8685
8686         if (replace && parsed_str) {
8687                 gtk_text_buffer_get_start_iter(buffer, &iter);
8688                 gtk_text_buffer_place_cursor(buffer, &iter);
8689         }
8690         
8691         if (parsed_str) {
8692                 cursor_pos = quote_fmt_get_cursor_pos();
8693                 compose->set_cursor_pos = cursor_pos;
8694                 if (cursor_pos == -1)
8695                         cursor_pos = 0;
8696                 gtk_text_buffer_get_start_iter(buffer, &iter);
8697                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
8698                 gtk_text_buffer_place_cursor(buffer, &iter);
8699         }
8700
8701         /* process the other fields */
8702
8703         compose_template_apply_fields(compose, tmpl);
8704         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
8705         quote_fmt_reset_vartable();
8706         compose_changed_cb(NULL, compose);
8707
8708 #ifdef USE_ENCHANT
8709         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
8710                 gtkaspell_highlight_all(compose->gtkaspell);
8711 #endif
8712 }
8713
8714 static void compose_template_apply_fields_error(const gchar *header)
8715 {
8716         gchar *tr;
8717         gchar *text;
8718
8719         tr = g_strdup(C_("'%s' stands for a header name",
8720                                   "Template '%s' format error."));
8721         text = g_strdup_printf(tr, prefs_common_translated_header_name(header));
8722         alertpanel_error(text);
8723
8724         g_free(text);
8725         g_free(tr);
8726 }
8727
8728 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
8729 {
8730         MsgInfo* dummyinfo = NULL;
8731         MsgInfo *msginfo = NULL;
8732         gchar *buf = NULL;
8733
8734         if (compose->replyinfo != NULL)
8735                 msginfo = compose->replyinfo;
8736         else if (compose->fwdinfo != NULL)
8737                 msginfo = compose->fwdinfo;
8738         else {
8739                 dummyinfo = compose_msginfo_new_from_compose(compose);
8740                 msginfo = dummyinfo;
8741         }
8742
8743         if (tmpl->from && *tmpl->from != '\0') {
8744 #ifdef USE_ENCHANT
8745                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8746                                 compose->gtkaspell);
8747 #else
8748                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8749 #endif
8750                 quote_fmt_scan_string(tmpl->from);
8751                 quote_fmt_parse();
8752
8753                 buf = quote_fmt_get_buffer();
8754                 if (buf == NULL) {
8755                         compose_template_apply_fields_error("From");
8756                 } else {
8757                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
8758                 }
8759         }
8760
8761         if (tmpl->to && *tmpl->to != '\0') {
8762 #ifdef USE_ENCHANT
8763                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8764                                 compose->gtkaspell);
8765 #else
8766                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8767 #endif
8768                 quote_fmt_scan_string(tmpl->to);
8769                 quote_fmt_parse();
8770
8771                 buf = quote_fmt_get_buffer();
8772                 if (buf == NULL) {
8773                         compose_template_apply_fields_error("To");
8774                 } else {
8775                         compose_entry_append(compose, buf, COMPOSE_TO, PREF_TEMPLATE);
8776                 }
8777         }
8778
8779         if (tmpl->cc && *tmpl->cc != '\0') {
8780 #ifdef USE_ENCHANT
8781                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8782                                 compose->gtkaspell);
8783 #else
8784                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8785 #endif
8786                 quote_fmt_scan_string(tmpl->cc);
8787                 quote_fmt_parse();
8788
8789                 buf = quote_fmt_get_buffer();
8790                 if (buf == NULL) {
8791                         compose_template_apply_fields_error("Cc");
8792                 } else {
8793                         compose_entry_append(compose, buf, COMPOSE_CC, PREF_TEMPLATE);
8794                 }
8795         }
8796
8797         if (tmpl->bcc && *tmpl->bcc != '\0') {
8798 #ifdef USE_ENCHANT
8799                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8800                                 compose->gtkaspell);
8801 #else
8802                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8803 #endif
8804                 quote_fmt_scan_string(tmpl->bcc);
8805                 quote_fmt_parse();
8806
8807                 buf = quote_fmt_get_buffer();
8808                 if (buf == NULL) {
8809                         compose_template_apply_fields_error("Bcc");
8810                 } else {
8811                         compose_entry_append(compose, buf, COMPOSE_BCC, PREF_TEMPLATE);
8812                 }
8813         }
8814
8815         if (tmpl->replyto && *tmpl->replyto != '\0') {
8816 #ifdef USE_ENCHANT
8817                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8818                                 compose->gtkaspell);
8819 #else
8820                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8821 #endif
8822                 quote_fmt_scan_string(tmpl->replyto);
8823                 quote_fmt_parse();
8824
8825                 buf = quote_fmt_get_buffer();
8826                 if (buf == NULL) {
8827                         compose_template_apply_fields_error("Reply-To");
8828                 } else {
8829                         compose_entry_append(compose, buf, COMPOSE_REPLYTO, PREF_TEMPLATE);
8830                 }
8831         }
8832
8833         /* process the subject */
8834         if (tmpl->subject && *tmpl->subject != '\0') {
8835 #ifdef USE_ENCHANT
8836                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
8837                                 compose->gtkaspell);
8838 #else
8839                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
8840 #endif
8841                 quote_fmt_scan_string(tmpl->subject);
8842                 quote_fmt_parse();
8843
8844                 buf = quote_fmt_get_buffer();
8845                 if (buf == NULL) {
8846                         compose_template_apply_fields_error("Subject");
8847                 } else {
8848                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
8849                 }
8850         }
8851
8852         procmsg_msginfo_free( &dummyinfo );
8853 }
8854
8855 static void compose_destroy(Compose *compose)
8856 {
8857         GtkAllocation allocation;
8858         GtkTextBuffer *buffer;
8859         GtkClipboard *clipboard;
8860
8861         compose_list = g_list_remove(compose_list, compose);
8862
8863         if (compose->updating) {
8864                 debug_print("danger, not destroying anything now\n");
8865                 compose->deferred_destroy = TRUE;
8866                 return;
8867         }
8868
8869         /* NOTE: address_completion_end() does nothing with the window
8870          * however this may change. */
8871         address_completion_end(compose->window);
8872
8873         slist_free_strings_full(compose->to_list);
8874         slist_free_strings_full(compose->newsgroup_list);
8875         slist_free_strings_full(compose->header_list);
8876
8877         slist_free_strings_full(extra_headers);
8878         extra_headers = NULL;
8879
8880         compose->header_list = compose->newsgroup_list = compose->to_list = NULL;
8881
8882         g_hash_table_destroy(compose->email_hashtable);
8883
8884         hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST,
8885                         compose->folder_update_callback_id);
8886
8887         procmsg_msginfo_free(&(compose->targetinfo));
8888         procmsg_msginfo_free(&(compose->replyinfo));
8889         procmsg_msginfo_free(&(compose->fwdinfo));
8890
8891         g_free(compose->replyto);
8892         g_free(compose->cc);
8893         g_free(compose->bcc);
8894         g_free(compose->newsgroups);
8895         g_free(compose->followup_to);
8896
8897         g_free(compose->ml_post);
8898
8899         g_free(compose->inreplyto);
8900         g_free(compose->references);
8901         g_free(compose->msgid);
8902         g_free(compose->boundary);
8903
8904         g_free(compose->redirect_filename);
8905         if (compose->undostruct)
8906                 undo_destroy(compose->undostruct);
8907
8908         g_free(compose->sig_str);
8909
8910         g_free(compose->exteditor_file);
8911
8912         g_free(compose->orig_charset);
8913
8914         g_free(compose->privacy_system);
8915         g_free(compose->encdata);
8916
8917 #ifndef USE_ALT_ADDRBOOK
8918         if (addressbook_get_target_compose() == compose)
8919                 addressbook_set_target_compose(NULL);
8920 #endif
8921 #if USE_ENCHANT
8922         if (compose->gtkaspell) {
8923                 gtkaspell_delete(compose->gtkaspell);
8924                 compose->gtkaspell = NULL;
8925         }
8926 #endif
8927
8928         if (!compose->batch) {
8929                 gtk_widget_get_allocation(compose->window, &allocation);
8930                 prefs_common.compose_width = allocation.width;
8931                 prefs_common.compose_height = allocation.height;
8932         }
8933
8934         if (!gtk_widget_get_parent(compose->paned))
8935                 gtk_widget_destroy(compose->paned);
8936         gtk_widget_destroy(compose->popupmenu);
8937
8938         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8939         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8940         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8941
8942         gtk_widget_destroy(compose->window);
8943         toolbar_destroy(compose->toolbar);
8944         g_free(compose->toolbar);
8945         cm_mutex_free(compose->mutex);
8946         g_free(compose);
8947 }
8948
8949 static void compose_attach_info_free(AttachInfo *ainfo)
8950 {
8951         g_free(ainfo->file);
8952         g_free(ainfo->content_type);
8953         g_free(ainfo->name);
8954         g_free(ainfo->charset);
8955         g_free(ainfo);
8956 }
8957
8958 static void compose_attach_update_label(Compose *compose)
8959 {
8960         GtkTreeIter iter;
8961         gint i = 1;
8962         gchar *text;
8963         GtkTreeModel *model;
8964         
8965         if(compose == NULL)
8966                 return;
8967                 
8968         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8969         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8970                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8971                 return;
8972         }
8973         
8974         while(gtk_tree_model_iter_next(model, &iter))
8975                 i++;
8976         
8977         text = g_strdup_printf("(%d)", i);
8978         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8979         g_free(text);
8980 }
8981
8982 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8983 {
8984         Compose *compose = (Compose *)data;
8985         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8986         GtkTreeSelection *selection;
8987         GList *sel, *cur;
8988         GtkTreeModel *model;
8989
8990         selection = gtk_tree_view_get_selection(tree_view);
8991         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8992
8993         if (!sel) 
8994                 return;
8995
8996         for (cur = sel; cur != NULL; cur = cur->next) {
8997                 GtkTreePath *path = cur->data;
8998                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8999                                                 (model, cur->data);
9000                 cur->data = ref;
9001                 gtk_tree_path_free(path);
9002         }
9003
9004         for (cur = sel; cur != NULL; cur = cur->next) {
9005                 GtkTreeRowReference *ref = cur->data;
9006                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
9007                 GtkTreeIter iter;
9008
9009                 if (gtk_tree_model_get_iter(model, &iter, path))
9010                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
9011                 
9012                 gtk_tree_path_free(path);
9013                 gtk_tree_row_reference_free(ref);
9014         }
9015
9016         g_list_free(sel);
9017         compose_attach_update_label(compose);
9018 }
9019
9020 static struct _AttachProperty
9021 {
9022         GtkWidget *window;
9023         GtkWidget *mimetype_entry;
9024         GtkWidget *encoding_optmenu;
9025         GtkWidget *path_entry;
9026         GtkWidget *filename_entry;
9027         GtkWidget *ok_btn;
9028         GtkWidget *cancel_btn;
9029 } attach_prop;
9030
9031 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
9032 {       
9033         gtk_tree_path_free((GtkTreePath *)ptr);
9034 }
9035
9036 static void compose_attach_property(GtkAction *action, gpointer data)
9037 {
9038         Compose *compose = (Compose *)data;
9039         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
9040         AttachInfo *ainfo;
9041         GtkComboBox *optmenu;
9042         GtkTreeSelection *selection;
9043         GList *sel;
9044         GtkTreeModel *model;
9045         GtkTreeIter iter;
9046         GtkTreePath *path;
9047         static gboolean cancelled;
9048
9049         /* only if one selected */
9050         selection = gtk_tree_view_get_selection(tree_view);
9051         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
9052                 return;
9053
9054         sel = gtk_tree_selection_get_selected_rows(selection, &model);
9055         if (!sel)
9056                 return;
9057
9058         path = (GtkTreePath *) sel->data;
9059         gtk_tree_model_get_iter(model, &iter, path);
9060         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
9061         
9062         if (!ainfo) {
9063                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
9064                 g_list_free(sel);
9065                 return;
9066         }               
9067         g_list_free(sel);
9068
9069         if (!attach_prop.window)
9070                 compose_attach_property_create(&cancelled);
9071         gtk_window_set_modal(GTK_WINDOW(attach_prop.window), TRUE);
9072         gtk_widget_grab_focus(attach_prop.ok_btn);
9073         gtk_widget_show(attach_prop.window);
9074         gtk_window_set_transient_for(GTK_WINDOW(attach_prop.window),
9075                         GTK_WINDOW(compose->window));
9076
9077         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
9078         if (ainfo->encoding == ENC_UNKNOWN)
9079                 combobox_select_by_data(optmenu, ENC_BASE64);
9080         else
9081                 combobox_select_by_data(optmenu, ainfo->encoding);
9082
9083         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
9084                            ainfo->content_type ? ainfo->content_type : "");
9085         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
9086                            ainfo->file ? ainfo->file : "");
9087         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
9088                            ainfo->name ? ainfo->name : "");
9089
9090         for (;;) {
9091                 const gchar *entry_text;
9092                 gchar *text;
9093                 gchar *cnttype = NULL;
9094                 gchar *file = NULL;
9095                 off_t size = 0;
9096
9097                 cancelled = FALSE;
9098                 gtk_main();
9099
9100                 gtk_widget_hide(attach_prop.window);
9101                 gtk_window_set_modal(GTK_WINDOW(attach_prop.window), FALSE);
9102                 
9103                 if (cancelled)
9104                         break;
9105
9106                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
9107                 if (*entry_text != '\0') {
9108                         gchar *p;
9109
9110                         text = g_strstrip(g_strdup(entry_text));
9111                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
9112                                 cnttype = g_strdup(text);
9113                                 g_free(text);
9114                         } else {
9115                                 alertpanel_error(_("Invalid MIME type."));
9116                                 g_free(text);
9117                                 continue;
9118                         }
9119                 }
9120
9121                 ainfo->encoding = combobox_get_active_data(optmenu);
9122
9123                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
9124                 if (*entry_text != '\0') {
9125                         if (is_file_exist(entry_text) &&
9126                             (size = get_file_size(entry_text)) > 0)
9127                                 file = g_strdup(entry_text);
9128                         else {
9129                                 alertpanel_error
9130                                         (_("File doesn't exist or is empty."));
9131                                 g_free(cnttype);
9132                                 continue;
9133                         }
9134                 }
9135
9136                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
9137                 if (*entry_text != '\0') {
9138                         g_free(ainfo->name);
9139                         ainfo->name = g_strdup(entry_text);
9140                 }
9141
9142                 if (cnttype) {
9143                         g_free(ainfo->content_type);
9144                         ainfo->content_type = cnttype;
9145                 }
9146                 if (file) {
9147                         g_free(ainfo->file);
9148                         ainfo->file = file;
9149                 }
9150                 if (size)
9151                         ainfo->size = (goffset)size;
9152
9153                 /* update tree store */
9154                 text = to_human_readable(ainfo->size);
9155                 gtk_tree_model_get_iter(model, &iter, path);
9156                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
9157                                    COL_MIMETYPE, ainfo->content_type,
9158                                    COL_SIZE, text,
9159                                    COL_NAME, ainfo->name,
9160                                    COL_CHARSET, ainfo->charset,
9161                                    -1);
9162                 
9163                 break;
9164         }
9165
9166         gtk_tree_path_free(path);
9167 }
9168
9169 #define SET_LABEL_AND_ENTRY(str, entry, top) \
9170 { \
9171         label = gtk_label_new(str); \
9172         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
9173                          GTK_FILL, 0, 0, 0); \
9174         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
9175  \
9176         entry = gtk_entry_new(); \
9177         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
9178                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
9179 }
9180
9181 static void compose_attach_property_create(gboolean *cancelled)
9182 {
9183         GtkWidget *window;
9184         GtkWidget *vbox;
9185         GtkWidget *table;
9186         GtkWidget *label;
9187         GtkWidget *mimetype_entry;
9188         GtkWidget *hbox;
9189         GtkWidget *optmenu;
9190         GtkListStore *optmenu_menu;
9191         GtkWidget *path_entry;
9192         GtkWidget *filename_entry;
9193         GtkWidget *hbbox;
9194         GtkWidget *ok_btn;
9195         GtkWidget *cancel_btn;
9196         GList     *mime_type_list, *strlist;
9197         GtkTreeIter iter;
9198
9199         debug_print("Creating attach_property window...\n");
9200
9201         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
9202         gtk_widget_set_size_request(window, 480, -1);
9203         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
9204         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
9205         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
9206         g_signal_connect(G_OBJECT(window), "delete_event",
9207                          G_CALLBACK(attach_property_delete_event),
9208                          cancelled);
9209         g_signal_connect(G_OBJECT(window), "key_press_event",
9210                          G_CALLBACK(attach_property_key_pressed),
9211                          cancelled);
9212
9213         vbox = gtk_vbox_new(FALSE, 8);
9214         gtk_container_add(GTK_CONTAINER(window), vbox);
9215
9216         table = gtk_table_new(4, 2, FALSE);
9217         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
9218         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
9219         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
9220
9221         label = gtk_label_new(_("MIME type")); 
9222         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
9223                          GTK_FILL, 0, 0, 0); 
9224         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
9225 #if !GTK_CHECK_VERSION(2, 24, 0)
9226         mimetype_entry = gtk_combo_box_entry_new_text(); 
9227 #else
9228         mimetype_entry = gtk_combo_box_text_new_with_entry();
9229 #endif
9230         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
9231                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9232                          
9233         /* stuff with list */
9234         mime_type_list = procmime_get_mime_type_list();
9235         strlist = NULL;
9236         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
9237                 MimeType *type = (MimeType *) mime_type_list->data;
9238                 gchar *tmp;
9239
9240                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
9241
9242                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
9243                         g_free(tmp);
9244                 else
9245                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
9246                                         (GCompareFunc)strcmp2);
9247         }
9248
9249         for (mime_type_list = strlist; mime_type_list != NULL; 
9250                 mime_type_list = mime_type_list->next) {
9251 #if !GTK_CHECK_VERSION(2, 24, 0)
9252                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
9253 #else
9254                 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(mimetype_entry), mime_type_list->data);
9255 #endif
9256                 g_free(mime_type_list->data);
9257         }
9258         g_list_free(strlist);
9259         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
9260         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
9261
9262         label = gtk_label_new(_("Encoding"));
9263         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
9264                          GTK_FILL, 0, 0, 0);
9265         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
9266
9267         hbox = gtk_hbox_new(FALSE, 0);
9268         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
9269                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
9270
9271         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
9272         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
9273
9274         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
9275         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
9276         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
9277         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
9278         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
9279
9280         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
9281
9282         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
9283         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
9284
9285         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
9286                                       &ok_btn, GTK_STOCK_OK,
9287                                       NULL, NULL);
9288         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
9289         gtk_widget_grab_default(ok_btn);
9290
9291         g_signal_connect(G_OBJECT(ok_btn), "clicked",
9292                          G_CALLBACK(attach_property_ok),
9293                          cancelled);
9294         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
9295                          G_CALLBACK(attach_property_cancel),
9296                          cancelled);
9297
9298         gtk_widget_show_all(vbox);
9299
9300         attach_prop.window           = window;
9301         attach_prop.mimetype_entry   = mimetype_entry;
9302         attach_prop.encoding_optmenu = optmenu;
9303         attach_prop.path_entry       = path_entry;
9304         attach_prop.filename_entry   = filename_entry;
9305         attach_prop.ok_btn           = ok_btn;
9306         attach_prop.cancel_btn       = cancel_btn;
9307 }
9308
9309 #undef SET_LABEL_AND_ENTRY
9310
9311 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
9312 {
9313         *cancelled = FALSE;
9314         gtk_main_quit();
9315 }
9316
9317 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
9318 {
9319         *cancelled = TRUE;
9320         gtk_main_quit();
9321 }
9322
9323 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
9324                                          gboolean *cancelled)
9325 {
9326         *cancelled = TRUE;
9327         gtk_main_quit();
9328
9329         return TRUE;
9330 }
9331
9332 static gboolean attach_property_key_pressed(GtkWidget *widget,
9333                                             GdkEventKey *event,
9334                                             gboolean *cancelled)
9335 {
9336         if (event && event->keyval == GDK_KEY_Escape) {
9337                 *cancelled = TRUE;
9338                 gtk_main_quit();
9339         }
9340         if (event && event->keyval == GDK_KEY_Return) {
9341                 *cancelled = FALSE;
9342                 gtk_main_quit();
9343                 return TRUE;
9344         }
9345         return FALSE;
9346 }
9347
9348 static void compose_exec_ext_editor(Compose *compose)
9349 {
9350 #ifdef G_OS_UNIX
9351         gchar *tmp;
9352         GtkWidget *socket;
9353         GdkNativeWindow socket_wid = 0;
9354         pid_t pid;
9355         gint pipe_fds[2];
9356
9357         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
9358                               G_DIR_SEPARATOR, compose);
9359
9360         if (compose_get_ext_editor_uses_socket()) {
9361                 /* Only allow one socket */
9362                 if (compose->exteditor_socket != NULL) {
9363                         if (gtk_widget_is_focus(compose->exteditor_socket)) {
9364                                 /* Move the focus off of the socket */
9365                                 gtk_widget_child_focus(compose->window, GTK_DIR_TAB_BACKWARD);
9366                         }
9367                         g_free(tmp);
9368                         return;
9369                 }
9370                 /* Create the receiving GtkSocket */
9371                 socket = gtk_socket_new ();
9372                 g_signal_connect (GTK_OBJECT(socket), "plug-removed",
9373                                   G_CALLBACK(compose_ext_editor_plug_removed_cb),
9374                                   compose);
9375                 gtk_box_pack_start(GTK_BOX(compose->edit_vbox), socket, TRUE, TRUE, 0);
9376                 gtk_widget_set_size_request(socket, prefs_common.compose_width, -1);
9377                 /* Realize the socket so that we can use its ID */
9378                 gtk_widget_realize(socket);
9379                 socket_wid = gtk_socket_get_id(GTK_SOCKET (socket));
9380                 compose->exteditor_socket = socket;
9381         }
9382
9383         if (pipe(pipe_fds) < 0) {
9384                 perror("pipe");
9385                 g_free(tmp);
9386                 return;
9387         }
9388
9389         if ((pid = fork()) < 0) {
9390                 perror("fork");
9391                 g_free(tmp);
9392                 return;
9393         }
9394
9395         if (pid != 0) {
9396                 /* close the write side of the pipe */
9397                 close(pipe_fds[1]);
9398
9399                 compose->exteditor_file    = g_strdup(tmp);
9400                 compose->exteditor_pid     = pid;
9401
9402                 compose_set_ext_editor_sensitive(compose, FALSE);
9403
9404 #ifndef G_OS_WIN32
9405                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
9406 #else
9407                 compose->exteditor_ch = g_io_channel_win32_new_fd(pipe_fds[0]);
9408 #endif
9409                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
9410                                                         G_IO_IN,
9411                                                         compose_input_cb,
9412                                                         compose);
9413         } else {        /* process-monitoring process */
9414                 pid_t pid_ed;
9415
9416                 if (setpgid(0, 0))
9417                         perror("setpgid");
9418
9419                 /* close the read side of the pipe */
9420                 close(pipe_fds[0]);
9421
9422                 if (compose_write_body_to_file(compose, tmp) < 0) {
9423                         fd_write_all(pipe_fds[1], "2\n", 2);
9424                         _exit(1);
9425                 }
9426
9427                 pid_ed = compose_exec_ext_editor_real(tmp, socket_wid);
9428                 if (pid_ed < 0) {
9429                         fd_write_all(pipe_fds[1], "1\n", 2);
9430                         _exit(1);
9431                 }
9432
9433                 /* wait until editor is terminated */
9434                 waitpid(pid_ed, NULL, 0);
9435
9436                 fd_write_all(pipe_fds[1], "0\n", 2);
9437
9438                 close(pipe_fds[1]);
9439                 _exit(0);
9440         }
9441
9442         g_free(tmp);
9443 #endif /* G_OS_UNIX */
9444 }
9445
9446 static gboolean compose_can_autosave(Compose *compose)
9447 {
9448         if (compose->privacy_system && compose->use_encryption)
9449                 return prefs_common.autosave && prefs_common.autosave_encrypted;
9450         else
9451                 return prefs_common.autosave;
9452 }
9453
9454 #ifdef G_OS_UNIX
9455 static gboolean compose_get_ext_editor_cmd_valid()
9456 {
9457         gboolean has_s = FALSE;
9458         gboolean has_w = FALSE;
9459         const gchar *p = prefs_common_get_ext_editor_cmd();
9460         if (!p)
9461                 return FALSE;
9462         while ((p = strchr(p, '%'))) {
9463                 p++;
9464                 if (*p == 's') {
9465                         if (has_s)
9466                                 return FALSE;
9467                         has_s = TRUE;
9468                 } else if (*p == 'w') {
9469                         if (has_w)
9470                                 return FALSE;
9471                         has_w = TRUE;
9472                 } else {
9473                         return FALSE;
9474                 }
9475         }
9476         return TRUE;
9477 }
9478
9479 static gint compose_exec_ext_editor_real(const gchar *file, GdkNativeWindow socket_wid)
9480 {
9481         gchar *buf;
9482         gchar *p, *s;
9483         gchar **cmdline;
9484         pid_t pid;
9485
9486         cm_return_val_if_fail(file != NULL, -1);
9487
9488         if ((pid = fork()) < 0) {
9489                 perror("fork");
9490                 return -1;
9491         }
9492
9493         if (pid != 0) return pid;
9494
9495         /* grandchild process */
9496
9497         if (setpgid(0, getppid()))
9498                 perror("setpgid");
9499
9500         if (compose_get_ext_editor_cmd_valid()) {
9501                 if (compose_get_ext_editor_uses_socket()) {
9502                         p = g_strdup(prefs_common_get_ext_editor_cmd());
9503                         s = strstr(p, "%w");
9504                         s[1] = 'u';
9505                         if (strstr(p, "%s") < s)
9506                                 buf = g_strdup_printf(p, file, socket_wid);
9507                         else
9508                                 buf = g_strdup_printf(p, socket_wid, file);
9509                         g_free(p);
9510                 } else {
9511                         buf = g_strdup_printf(prefs_common_get_ext_editor_cmd(), file);
9512                 }
9513         } else {
9514                 if (prefs_common_get_ext_editor_cmd())
9515                         g_warning("External editor command-line is invalid: '%s'",
9516                                   prefs_common_get_ext_editor_cmd());
9517                 buf = g_strdup_printf(DEFAULT_EDITOR_CMD, file);
9518         }
9519
9520         cmdline = strsplit_with_quote(buf, " ", 0);
9521         g_free(buf);
9522         execvp(cmdline[0], cmdline);
9523
9524         perror("execvp");
9525         g_strfreev(cmdline);
9526
9527         _exit(1);
9528 }
9529
9530 static gboolean compose_ext_editor_kill(Compose *compose)
9531 {
9532         pid_t pgid = compose->exteditor_pid * -1;
9533         gint ret;
9534
9535         ret = kill(pgid, 0);
9536
9537         if (ret == 0 || (ret == -1 && EPERM == errno)) {
9538                 AlertValue val;
9539                 gchar *msg;
9540
9541                 msg = g_strdup_printf
9542                         (_("The external editor is still working.\n"
9543                            "Force terminating the process?\n"
9544                            "process group id: %d"), -pgid);
9545                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
9546                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
9547                         
9548                 g_free(msg);
9549
9550                 if (val == G_ALERTALTERNATE) {
9551                         g_source_remove(compose->exteditor_tag);
9552                         g_io_channel_shutdown(compose->exteditor_ch,
9553                                               FALSE, NULL);
9554                         g_io_channel_unref(compose->exteditor_ch);
9555
9556                         if (kill(pgid, SIGTERM) < 0) perror("kill");
9557                         waitpid(compose->exteditor_pid, NULL, 0);
9558
9559                         g_warning("Terminated process group id: %d. "
9560                                   "Temporary file: %s", -pgid, compose->exteditor_file);
9561
9562                         compose_set_ext_editor_sensitive(compose, TRUE);
9563
9564                         g_free(compose->exteditor_file);
9565                         compose->exteditor_file    = NULL;
9566                         compose->exteditor_pid     = -1;
9567                         compose->exteditor_ch      = NULL;
9568                         compose->exteditor_tag     = -1;
9569                 } else
9570                         return FALSE;
9571         }
9572
9573         return TRUE;
9574 }
9575
9576 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
9577                                  gpointer data)
9578 {
9579         gchar buf[3] = "3";
9580         Compose *compose = (Compose *)data;
9581         gsize bytes_read;
9582
9583         debug_print("Compose: input from monitoring process\n");
9584
9585         if (g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL) != G_IO_STATUS_NORMAL) {
9586                 bytes_read = 0;
9587                 buf[0] = '\0';
9588         }
9589
9590         g_io_channel_shutdown(source, FALSE, NULL);
9591         g_io_channel_unref(source);
9592
9593         waitpid(compose->exteditor_pid, NULL, 0);
9594
9595         if (buf[0] == '0') {            /* success */
9596                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9597                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
9598                 GtkTextIter start, end;
9599                 gchar *chars;
9600
9601                 gtk_text_buffer_set_text(buffer, "", -1);
9602                 compose_insert_file(compose, compose->exteditor_file);
9603                 compose_changed_cb(NULL, compose);
9604
9605                 /* Check if we should save the draft or not */
9606                 if (compose_can_autosave(compose))
9607                   compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
9608
9609                 if (claws_unlink(compose->exteditor_file) < 0)
9610                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9611
9612                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
9613                 gtk_text_buffer_get_start_iter(buffer, &start);
9614                 gtk_text_buffer_get_end_iter(buffer, &end);
9615                 chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
9616                 if (chars && strlen(chars) > 0)
9617                         compose->modified = TRUE;
9618                 g_free(chars);
9619         } else if (buf[0] == '1') {     /* failed */
9620                 g_warning("Couldn't exec external editor");
9621                 if (claws_unlink(compose->exteditor_file) < 0)
9622                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
9623         } else if (buf[0] == '2') {
9624                 g_warning("Couldn't write to file");
9625         } else if (buf[0] == '3') {
9626                 g_warning("Pipe read failed");
9627         }
9628
9629         compose_set_ext_editor_sensitive(compose, TRUE);
9630
9631         g_free(compose->exteditor_file);
9632         compose->exteditor_file    = NULL;
9633         compose->exteditor_pid     = -1;
9634         compose->exteditor_ch      = NULL;
9635         compose->exteditor_tag     = -1;
9636         if (compose->exteditor_socket) {
9637                 gtk_widget_destroy(compose->exteditor_socket);
9638                 compose->exteditor_socket = NULL;
9639         }
9640
9641
9642         return FALSE;
9643 }
9644
9645 static char *ext_editor_menu_entries[] = {
9646         "Menu/Message/Send",
9647         "Menu/Message/SendLater",
9648         "Menu/Message/InsertFile",
9649         "Menu/Message/InsertSig",
9650         "Menu/Message/ReplaceSig",
9651         "Menu/Message/Save",
9652         "Menu/Message/Print",
9653         "Menu/Edit",
9654 #if USE_ENCHANT
9655         "Menu/Spelling",
9656 #endif
9657         "Menu/Tools/ShowRuler",
9658         "Menu/Tools/Actions",
9659         "Menu/Help",
9660         NULL
9661 };
9662
9663 static void compose_set_ext_editor_sensitive(Compose *compose,
9664                                              gboolean sensitive)
9665 {
9666         int i;
9667
9668         for (i = 0; ext_editor_menu_entries[i]; ++i) {
9669                 cm_menu_set_sensitive_full(compose->ui_manager,
9670                         ext_editor_menu_entries[i], sensitive);
9671         }
9672
9673         if (compose_get_ext_editor_uses_socket()) {
9674                 if (sensitive) {
9675                         if (compose->exteditor_socket)
9676                                 gtk_widget_hide(compose->exteditor_socket);
9677                         gtk_widget_show(compose->scrolledwin);
9678                         if (prefs_common.show_ruler)
9679                                 gtk_widget_show(compose->ruler_hbox);
9680                         /* Fix the focus, as it doesn't go anywhere when the
9681                          * socket is hidden or destroyed */
9682                         gtk_widget_child_focus(compose->window, GTK_DIR_TAB_BACKWARD);
9683                 } else {
9684                         g_assert (compose->exteditor_socket != NULL);
9685                         /* Fix the focus, as it doesn't go anywhere when the
9686                          * edit box is hidden */
9687                         if (gtk_widget_is_focus(compose->text))
9688                                 gtk_widget_child_focus(compose->window, GTK_DIR_TAB_BACKWARD);
9689                         gtk_widget_hide(compose->scrolledwin);
9690                         gtk_widget_hide(compose->ruler_hbox);
9691                         gtk_widget_show(compose->exteditor_socket);
9692                 }
9693         } else {
9694                 gtk_widget_set_sensitive(compose->text,                   sensitive);
9695         }
9696         if (compose->toolbar->send_btn)
9697                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
9698         if (compose->toolbar->sendl_btn)
9699                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
9700         if (compose->toolbar->draft_btn)
9701                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
9702         if (compose->toolbar->insert_btn)
9703                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
9704         if (compose->toolbar->sig_btn)
9705                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
9706         if (compose->toolbar->exteditor_btn)
9707                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
9708         if (compose->toolbar->linewrap_current_btn)
9709                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
9710         if (compose->toolbar->linewrap_all_btn)
9711                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
9712 }
9713
9714 static gboolean compose_get_ext_editor_uses_socket()
9715 {
9716         return (prefs_common_get_ext_editor_cmd() &&
9717                 strstr(prefs_common_get_ext_editor_cmd(), "%w"));
9718 }
9719
9720 static gboolean compose_ext_editor_plug_removed_cb(GtkSocket *socket, Compose *compose)
9721 {
9722         compose->exteditor_socket = NULL;
9723         /* returning FALSE allows destruction of the socket */
9724         return FALSE;
9725 }
9726 #endif /* G_OS_UNIX */
9727
9728 /**
9729  * compose_undo_state_changed:
9730  *
9731  * Change the sensivity of the menuentries undo and redo
9732  **/
9733 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
9734                                        gint redo_state, gpointer data)
9735 {
9736         Compose *compose = (Compose *)data;
9737
9738         switch (undo_state) {
9739         case UNDO_STATE_TRUE:
9740                 if (!undostruct->undo_state) {
9741                         undostruct->undo_state = TRUE;
9742                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
9743                 }
9744                 break;
9745         case UNDO_STATE_FALSE:
9746                 if (undostruct->undo_state) {
9747                         undostruct->undo_state = FALSE;
9748                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
9749                 }
9750                 break;
9751         case UNDO_STATE_UNCHANGED:
9752                 break;
9753         case UNDO_STATE_REFRESH:
9754                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
9755                 break;
9756         default:
9757                 g_warning("Undo state not recognized");
9758                 break;
9759         }
9760
9761         switch (redo_state) {
9762         case UNDO_STATE_TRUE:
9763                 if (!undostruct->redo_state) {
9764                         undostruct->redo_state = TRUE;
9765                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
9766                 }
9767                 break;
9768         case UNDO_STATE_FALSE:
9769                 if (undostruct->redo_state) {
9770                         undostruct->redo_state = FALSE;
9771                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
9772                 }
9773                 break;
9774         case UNDO_STATE_UNCHANGED:
9775                 break;
9776         case UNDO_STATE_REFRESH:
9777                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
9778                 break;
9779         default:
9780                 g_warning("Redo state not recognized");
9781                 break;
9782         }
9783 }
9784
9785 /* callback functions */
9786
9787 static void compose_notebook_size_alloc(GtkNotebook *notebook,
9788                                         GtkAllocation *allocation,
9789                                         GtkPaned *paned)
9790 {
9791         prefs_common.compose_notebook_height = gtk_paned_get_position(paned);
9792 }
9793
9794 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
9795  * includes "non-client" (windows-izm) in calculation, so this calculation
9796  * may not be accurate.
9797  */
9798 static gboolean compose_edit_size_alloc(GtkEditable *widget,
9799                                         GtkAllocation *allocation,
9800                                         GtkSHRuler *shruler)
9801 {
9802         if (prefs_common.show_ruler) {
9803                 gint char_width = 0, char_height = 0;
9804                 gint line_width_in_chars;
9805
9806                 gtkut_get_font_size(GTK_WIDGET(widget),
9807                                     &char_width, &char_height);
9808                 line_width_in_chars =
9809                         (allocation->width - allocation->x) / char_width;
9810
9811                 /* got the maximum */
9812                 gtk_shruler_set_range(GTK_SHRULER(shruler),
9813                                     0.0, line_width_in_chars, 0);
9814         }
9815
9816         return TRUE;
9817 }
9818
9819 typedef struct {
9820         gchar                   *header;
9821         gchar                   *entry;
9822         ComposePrefType         type;
9823         gboolean                entry_marked;
9824 } HeaderEntryState;
9825
9826 static void account_activated(GtkComboBox *optmenu, gpointer data)
9827 {
9828         Compose *compose = (Compose *)data;
9829
9830         PrefsAccount *ac;
9831         gchar *folderidentifier;
9832         gint account_id = 0;
9833         GtkTreeModel *menu;
9834         GtkTreeIter iter;
9835         GSList *list, *saved_list = NULL;
9836         HeaderEntryState *state;
9837
9838         /* Get ID of active account in the combo box */
9839         menu = gtk_combo_box_get_model(optmenu);
9840         cm_return_if_fail(gtk_combo_box_get_active_iter(optmenu, &iter));
9841         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
9842
9843         ac = account_find_from_id(account_id);
9844         cm_return_if_fail(ac != NULL);
9845
9846         if (ac != compose->account) {
9847                 compose_select_account(compose, ac, FALSE);
9848
9849                 for (list = compose->header_list; list; list = list->next) {
9850                         ComposeHeaderEntry *hentry=(ComposeHeaderEntry *)list->data;
9851                         
9852                         if (hentry->type == PREF_ACCOUNT || !list->next) {
9853                                 compose_destroy_headerentry(compose, hentry);
9854                                 continue;
9855                         }
9856                         state = g_malloc0(sizeof(HeaderEntryState));
9857                         state->header = gtk_editable_get_chars(GTK_EDITABLE(
9858                                         gtk_bin_get_child(GTK_BIN(hentry->combo))), 0, -1);
9859                         state->entry = gtk_editable_get_chars(
9860                                         GTK_EDITABLE(hentry->entry), 0, -1);
9861                         state->type = hentry->type;
9862
9863                         saved_list = g_slist_append(saved_list, state);
9864                         compose_destroy_headerentry(compose, hentry);
9865                 }
9866
9867                 compose->header_last = NULL;
9868                 g_slist_free(compose->header_list);
9869                 compose->header_list = NULL;
9870                 compose->header_nextrow = 1;
9871                 compose_create_header_entry(compose);
9872                 
9873                 if (ac->set_autocc && ac->auto_cc)
9874                         compose_entry_append(compose, ac->auto_cc,
9875                                                 COMPOSE_CC, PREF_ACCOUNT);
9876                 if (ac->set_autobcc && ac->auto_bcc)
9877                         compose_entry_append(compose, ac->auto_bcc,
9878                                                 COMPOSE_BCC, PREF_ACCOUNT);
9879                 if (ac->set_autoreplyto && ac->auto_replyto)
9880                         compose_entry_append(compose, ac->auto_replyto,
9881                                                 COMPOSE_REPLYTO, PREF_ACCOUNT);
9882                 
9883                 for (list = saved_list; list; list = list->next) {
9884                         state = (HeaderEntryState *) list->data;
9885
9886                         compose_add_header_entry(compose, state->header,
9887                                                 state->entry, state->type);
9888
9889                         g_free(state->header);
9890                         g_free(state->entry);
9891                         g_free(state);
9892                 }
9893                 g_slist_free(saved_list);
9894
9895                 combobox_select_by_data(GTK_COMBO_BOX(compose->header_last->combo),
9896                                         (ac->protocol == A_NNTP) ? 
9897                                         COMPOSE_NEWSGROUPS : COMPOSE_TO);
9898         }
9899
9900         /* Set message save folder */
9901         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9902                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
9903         }
9904         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
9905                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
9906                            
9907         compose_set_save_to(compose, NULL);
9908         if (account_get_special_folder(compose->account, F_OUTBOX)) {
9909                 folderidentifier = folder_item_get_identifier(account_get_special_folder
9910                                   (compose->account, F_OUTBOX));
9911                 compose_set_save_to(compose, folderidentifier);
9912                 g_free(folderidentifier);
9913         }
9914 }
9915
9916 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
9917                             GtkTreeViewColumn *column, Compose *compose)
9918 {
9919         compose_attach_property(NULL, compose);
9920 }
9921
9922 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
9923                                       gpointer data)
9924 {
9925         Compose *compose = (Compose *)data;
9926         GtkTreeSelection *attach_selection;
9927         gint attach_nr_selected;
9928         GtkTreePath *path;
9929         
9930         if (!event) return FALSE;
9931
9932         if (event->button == 3) {
9933                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
9934                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
9935
9936                 /* If no rows, or just one row is selected, right-click should
9937                  * open menu relevant to the row being right-clicked on. We
9938                  * achieve that by selecting the clicked row first. If more
9939                  * than one row is selected, we shouldn't modify the selection,
9940                  * as user may want to remove selected rows (attachments). */
9941                 if (attach_nr_selected < 2) {
9942                         gtk_tree_selection_unselect_all(attach_selection);
9943                         attach_nr_selected = 0;
9944                         gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
9945                                         event->x, event->y, &path, NULL, NULL, NULL);
9946                         if (path != NULL) {
9947                                 gtk_tree_selection_select_path(attach_selection, path);
9948                                 gtk_tree_path_free(path);
9949                                 attach_nr_selected++;
9950                         }
9951                 }
9952
9953                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", (attach_nr_selected > 0));
9954                 /* Properties menu item makes no sense with more than one row
9955                  * selected, the properties dialog can only edit one attachment. */
9956                 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", (attach_nr_selected == 1));
9957                         
9958                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
9959                                NULL, NULL, event->button, event->time);
9960                 return TRUE;                           
9961         }
9962
9963         return FALSE;
9964 }
9965
9966 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
9967                                    gpointer data)
9968 {
9969         Compose *compose = (Compose *)data;
9970
9971         if (!event) return FALSE;
9972
9973         switch (event->keyval) {
9974         case GDK_KEY_Delete:
9975                 compose_attach_remove_selected(NULL, compose);
9976                 break;
9977         }
9978         return FALSE;
9979 }
9980
9981 static void compose_allow_user_actions (Compose *compose, gboolean allow)
9982 {
9983         toolbar_comp_set_sensitive(compose, allow);
9984         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
9985         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
9986 #if USE_ENCHANT
9987         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
9988 #endif  
9989         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
9990         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
9991         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
9992         
9993         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
9994
9995 }
9996
9997 static void compose_send_cb(GtkAction *action, gpointer data)
9998 {
9999         Compose *compose = (Compose *)data;
10000
10001 #ifdef G_OS_UNIX
10002         if (compose->exteditor_tag != -1) {
10003                 debug_print("ignoring send: external editor still open\n");
10004                 return;
10005         }
10006 #endif
10007         if (prefs_common.work_offline && 
10008             !inc_offline_should_override(TRUE,
10009                 _("Claws Mail needs network access in order "
10010                   "to send this email.")))
10011                 return;
10012         
10013         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
10014                 g_source_remove(compose->draft_timeout_tag);
10015                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
10016         }
10017
10018         compose_send(compose);
10019 }
10020
10021 static void compose_send_later_cb(GtkAction *action, gpointer data)
10022 {
10023         Compose *compose = (Compose *)data;
10024         gint val;
10025
10026         inc_lock();
10027         compose_allow_user_actions(compose, FALSE);
10028         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
10029         compose_allow_user_actions(compose, TRUE);
10030         inc_unlock();
10031
10032         if (!val) {
10033                 compose_close(compose);
10034         } else if (val == -1) {
10035                 alertpanel_error(_("Could not queue message."));
10036         } else if (val == -2) {
10037                 alertpanel_error(_("Could not queue message:\n\n%s."), g_strerror(errno));
10038         } else if (val == -3) {
10039                 if (privacy_peek_error())
10040                 alertpanel_error(_("Could not queue message for sending:\n\n"
10041                                    "Signature failed: %s"), privacy_get_error());
10042         } else if (val == -4) {
10043                 alertpanel_error(_("Could not queue message for sending:\n\n"
10044                                    "Charset conversion failed."));
10045         } else if (val == -5) {
10046                 alertpanel_error(_("Could not queue message for sending:\n\n"
10047                                    "Couldn't get recipient encryption key."));
10048         } else if (val == -6) {
10049                 /* silent error */
10050         }
10051         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
10052 }
10053
10054 #define DRAFTED_AT_EXIT "drafted_at_exit"
10055 static void compose_register_draft(MsgInfo *info)
10056 {
10057         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10058                                       DRAFTED_AT_EXIT, NULL);
10059         FILE *fp = g_fopen(filepath, "ab");
10060         
10061         if (fp) {
10062                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
10063                                 info->msgnum);
10064                 fclose(fp);
10065         }
10066                 
10067         g_free(filepath);       
10068 }
10069
10070 gboolean compose_draft (gpointer data, guint action) 
10071 {
10072         Compose *compose = (Compose *)data;
10073         FolderItem *draft;
10074         gchar *tmp;
10075         gchar *sheaders;
10076         gint msgnum;
10077         MsgFlags flag = {0, 0};
10078         static gboolean lock = FALSE;
10079         MsgInfo *newmsginfo;
10080         FILE *fp;
10081         gboolean target_locked = FALSE;
10082         gboolean err = FALSE;
10083
10084         if (lock) return FALSE;
10085
10086         if (compose->sending)
10087                 return TRUE;
10088
10089         draft = account_get_special_folder(compose->account, F_DRAFT);
10090         cm_return_val_if_fail(draft != NULL, FALSE);
10091         
10092         if (!g_mutex_trylock(compose->mutex)) {
10093                 /* we don't want to lock the mutex once it's available,
10094                  * because as the only other part of compose.c locking
10095                  * it is compose_close - which means once unlocked,
10096                  * the compose struct will be freed */
10097                 debug_print("couldn't lock mutex, probably sending\n");
10098                 return FALSE;
10099         }
10100
10101         lock = TRUE;
10102
10103         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
10104                               G_DIR_SEPARATOR, compose);
10105         if ((fp = g_fopen(tmp, "wb")) == NULL) {
10106                 FILE_OP_ERROR(tmp, "fopen");
10107                 goto warn_err;
10108         }
10109
10110         /* chmod for security */
10111         if (change_file_mode_rw(fp, tmp) < 0) {
10112                 FILE_OP_ERROR(tmp, "chmod");
10113                 g_warning("can't change file mode");
10114         }
10115
10116         /* Save draft infos */
10117         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
10118         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
10119
10120         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
10121                 gchar *savefolderid;
10122
10123                 savefolderid = compose_get_save_to(compose);
10124                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
10125                 g_free(savefolderid);
10126         }
10127         if (compose->return_receipt) {
10128                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
10129         }
10130         if (compose->privacy_system) {
10131                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
10132                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
10133                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
10134         }
10135
10136         /* Message-ID of message replying to */
10137         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
10138                 gchar *folderid = NULL;
10139
10140                 if (compose->replyinfo->folder)
10141                         folderid = folder_item_get_identifier(compose->replyinfo->folder);
10142                 if (folderid == NULL)
10143                         folderid = g_strdup("NULL");
10144
10145                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
10146                 g_free(folderid);
10147         }
10148         /* Message-ID of message forwarding to */
10149         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
10150                 gchar *folderid = NULL;
10151
10152                 if (compose->fwdinfo->folder)
10153                         folderid = folder_item_get_identifier(compose->fwdinfo->folder);
10154                 if (folderid == NULL)
10155                         folderid = g_strdup("NULL");
10156
10157                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
10158                 g_free(folderid);
10159         }
10160
10161         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
10162         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
10163
10164         sheaders = compose_get_manual_headers_info(compose);
10165         err |= (fprintf(fp, "X-Claws-Manual-Headers:%s\n", sheaders) < 0);
10166         g_free(sheaders);
10167
10168         /* end of headers */
10169         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
10170
10171         if (err) {
10172                 fclose(fp);
10173                 goto warn_err;
10174         }
10175
10176         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
10177                 fclose(fp);
10178                 goto warn_err;
10179         }
10180         if (fclose(fp) == EOF) {
10181                 goto warn_err;
10182         }
10183         
10184         flag.perm_flags = MSG_NEW|MSG_UNREAD;
10185         if (compose->targetinfo) {
10186                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
10187                 if (target_locked) 
10188                         flag.perm_flags |= MSG_LOCKED;
10189         }
10190         flag.tmp_flags = MSG_DRAFT;
10191
10192         folder_item_scan(draft);
10193         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
10194                 MsgInfo *tmpinfo = NULL;
10195                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
10196                 if (compose->msgid) {
10197                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
10198                 }
10199                 if (tmpinfo) {
10200                         msgnum = tmpinfo->msgnum;
10201                         procmsg_msginfo_free(&tmpinfo);
10202                         debug_print("got draft msgnum %d from scanning\n", msgnum);
10203                 } else {
10204                         debug_print("didn't get draft msgnum after scanning\n");
10205                 }
10206         } else {
10207                 debug_print("got draft msgnum %d from adding\n", msgnum);
10208         }
10209         if (msgnum < 0) {
10210 warn_err:
10211                 claws_unlink(tmp);
10212                 g_free(tmp);
10213                 if (action != COMPOSE_AUTO_SAVE) {
10214                         if (action != COMPOSE_DRAFT_FOR_EXIT)
10215                                 alertpanel_error(_("Could not save draft."));
10216                         else {
10217                                 AlertValue val;
10218                                 gtkut_window_popup(compose->window);
10219                                 val = alertpanel_full(_("Could not save draft"),
10220                                         _("Could not save draft.\n"
10221                                         "Do you want to cancel exit or discard this email?"),
10222                                           _("_Cancel exit"), _("_Discard email"), NULL,
10223                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
10224                                 if (val == G_ALERTALTERNATE) {
10225                                         lock = FALSE;
10226                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
10227                                         compose_close(compose);
10228                                         return TRUE;
10229                                 } else {
10230                                         lock = FALSE;
10231                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
10232                                         return FALSE;
10233                                 }
10234                         }
10235                 }
10236                 goto unlock;
10237         }
10238         g_free(tmp);
10239
10240         if (compose->mode == COMPOSE_REEDIT) {
10241                 compose_remove_reedit_target(compose, TRUE);
10242         }
10243
10244         newmsginfo = folder_item_get_msginfo(draft, msgnum);
10245
10246         if (newmsginfo) {
10247                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
10248                 if (target_locked)
10249                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD|MSG_LOCKED, MSG_DRAFT);
10250                 else
10251                         procmsg_msginfo_set_flags(newmsginfo, MSG_NEW|MSG_UNREAD, MSG_DRAFT);
10252                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
10253                         procmsg_msginfo_set_flags(newmsginfo, 0,
10254                                                   MSG_HAS_ATTACHMENT);
10255
10256                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
10257                         compose_register_draft(newmsginfo);
10258                 }
10259                 procmsg_msginfo_free(&newmsginfo);
10260         }
10261         
10262         folder_item_scan(draft);
10263         
10264         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
10265                 lock = FALSE;
10266                 g_mutex_unlock(compose->mutex); /* must be done before closing */
10267                 compose_close(compose);
10268                 return TRUE;
10269         } else {
10270                 GStatBuf s;
10271                 gchar *path;
10272
10273                 path = folder_item_fetch_msg(draft, msgnum);
10274                 if (path == NULL) {
10275                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
10276                         goto unlock;
10277                 }
10278                 if (g_stat(path, &s) < 0) {
10279                         FILE_OP_ERROR(path, "stat");
10280                         g_free(path);
10281                         goto unlock;
10282                 }
10283                 g_free(path);
10284
10285                 procmsg_msginfo_free(&(compose->targetinfo));
10286                 compose->targetinfo = procmsg_msginfo_new();
10287                 compose->targetinfo->msgnum = msgnum;
10288                 compose->targetinfo->size = (goffset)s.st_size;
10289                 compose->targetinfo->mtime = s.st_mtime;
10290                 compose->targetinfo->folder = draft;
10291                 if (target_locked)
10292                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
10293                 compose->mode = COMPOSE_REEDIT;
10294                 
10295                 if (action == COMPOSE_AUTO_SAVE) {
10296                         compose->autosaved_draft = compose->targetinfo;
10297                 }
10298                 compose->modified = FALSE;
10299                 compose_set_title(compose);
10300         }
10301 unlock:
10302         lock = FALSE;
10303         g_mutex_unlock(compose->mutex);
10304         return TRUE;
10305 }
10306
10307 void compose_clear_exit_drafts(void)
10308 {
10309         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10310                                       DRAFTED_AT_EXIT, NULL);
10311         if (is_file_exist(filepath))
10312                 claws_unlink(filepath);
10313         
10314         g_free(filepath);
10315 }
10316
10317 void compose_reopen_exit_drafts(void)
10318 {
10319         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
10320                                       DRAFTED_AT_EXIT, NULL);
10321         FILE *fp = g_fopen(filepath, "rb");
10322         gchar buf[1024];
10323         
10324         if (fp) {
10325                 while (fgets(buf, sizeof(buf), fp)) {
10326                         gchar **parts = g_strsplit(buf, "\t", 2);
10327                         const gchar *folder = parts[0];
10328                         int msgnum = parts[1] ? atoi(parts[1]):-1;
10329                         
10330                         if (folder && *folder && msgnum > -1) {
10331                                 FolderItem *item = folder_find_item_from_identifier(folder);
10332                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
10333                                 if (info)
10334                                         compose_reedit(info, FALSE);
10335                         }
10336                         g_strfreev(parts);
10337                 }       
10338                 fclose(fp);
10339         }       
10340         g_free(filepath);
10341         compose_clear_exit_drafts();
10342 }
10343
10344 static void compose_save_cb(GtkAction *action, gpointer data)
10345 {
10346         Compose *compose = (Compose *)data;
10347         compose_draft(compose, COMPOSE_KEEP_EDITING);
10348         compose->rmode = COMPOSE_REEDIT;
10349 }
10350
10351 void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
10352 {
10353         if (compose && file_list) {
10354                 GList *tmp;
10355
10356                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10357                         gchar *file = (gchar *) tmp->data;
10358                         gchar *utf8_filename = conv_filename_to_utf8(file);
10359                         compose_attach_append(compose, file, utf8_filename, NULL, NULL);
10360                         compose_changed_cb(NULL, compose);
10361                         if (free_data) {
10362                         g_free(file);
10363                                 tmp->data = NULL;
10364                         }
10365                         g_free(utf8_filename);
10366                 }
10367         }
10368 }
10369
10370 static void compose_attach_cb(GtkAction *action, gpointer data)
10371 {
10372         Compose *compose = (Compose *)data;
10373         GList *file_list;
10374
10375         if (compose->redirect_filename != NULL)
10376                 return;
10377
10378         /* Set focus_window properly, in case we were called via popup menu,
10379          * which unsets it (via focus_out_event callback on compose window). */
10380         manage_window_focus_in(compose->window, NULL, NULL);
10381
10382         file_list = filesel_select_multiple_files_open(_("Select file"), NULL);
10383
10384         if (file_list) {
10385                 compose_attach_from_list(compose, file_list, TRUE);
10386                 g_list_free(file_list);
10387         }
10388 }
10389
10390 static void compose_insert_file_cb(GtkAction *action, gpointer data)
10391 {
10392         Compose *compose = (Compose *)data;
10393         GList *file_list;
10394         gint files_inserted = 0;
10395
10396         file_list = filesel_select_multiple_files_open(_("Select file"), NULL);
10397
10398         if (file_list) {
10399                 GList *tmp;
10400
10401                 for ( tmp = file_list; tmp; tmp = tmp->next) {
10402                         gchar *file = (gchar *) tmp->data;
10403                         gchar *filedup = g_strdup(file);
10404                         gchar *shortfile = g_path_get_basename(filedup);
10405                         ComposeInsertResult res;
10406                         /* insert the file if the file is short or if the user confirmed that
10407                            he/she wants to insert the large file */
10408                         res = compose_insert_file(compose, file);
10409                         if (res == COMPOSE_INSERT_READ_ERROR) {
10410                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
10411                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
10412                                 alertpanel_error(_("File '%s' contained invalid characters\n"
10413                                                         "for the current encoding, insertion may be incorrect."),
10414                                                         shortfile);
10415                         } else if (res == COMPOSE_INSERT_SUCCESS)
10416                                 files_inserted++;
10417
10418                         g_free(shortfile);
10419                         g_free(filedup);
10420                         g_free(file);
10421                 }
10422                 g_list_free(file_list);
10423         }
10424
10425 #ifdef USE_ENCHANT      
10426         if (files_inserted > 0 && compose->gtkaspell && 
10427             compose->gtkaspell->check_while_typing)
10428                 gtkaspell_highlight_all(compose->gtkaspell);
10429 #endif
10430 }
10431
10432 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
10433 {
10434         Compose *compose = (Compose *)data;
10435
10436         compose_insert_sig(compose, FALSE);
10437 }
10438
10439 static void compose_replace_sig_cb(GtkAction *action, gpointer data)
10440 {
10441         Compose *compose = (Compose *)data;
10442
10443         compose_insert_sig(compose, TRUE);
10444 }
10445
10446 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
10447                               gpointer data)
10448 {
10449         gint x, y;
10450         Compose *compose = (Compose *)data;
10451
10452         gtkut_widget_get_uposition(widget, &x, &y);
10453         if (!compose->batch) {
10454                 prefs_common.compose_x = x;
10455                 prefs_common.compose_y = y;
10456         }
10457         if (compose->sending || compose->updating)
10458                 return TRUE;
10459         compose_close_cb(NULL, compose);
10460         return TRUE;
10461 }
10462
10463 void compose_close_toolbar(Compose *compose)
10464 {
10465         compose_close_cb(NULL, compose);
10466 }
10467
10468 static void compose_close_cb(GtkAction *action, gpointer data)
10469 {
10470         Compose *compose = (Compose *)data;
10471         AlertValue val;
10472
10473 #ifdef G_OS_UNIX
10474         if (compose->exteditor_tag != -1) {
10475                 if (!compose_ext_editor_kill(compose))
10476                         return;
10477         }
10478 #endif
10479
10480         if (compose->modified) {
10481                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
10482                 if (!g_mutex_trylock(compose->mutex)) {
10483                         /* we don't want to lock the mutex once it's available,
10484                          * because as the only other part of compose.c locking
10485                          * it is compose_close - which means once unlocked,
10486                          * the compose struct will be freed */
10487                         debug_print("couldn't lock mutex, probably sending\n");
10488                         return;
10489                 }
10490                 if (!reedit) {
10491                         val = alertpanel(_("Discard message"),
10492                                  _("This message has been modified. Discard it?"),
10493                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
10494                 } else {
10495                         val = alertpanel(_("Save changes"),
10496                                  _("This message has been modified. Save the latest changes?"),
10497                                  _("_Don't save"), g_strconcat("+", _("_Save to Drafts"), NULL),
10498                                 GTK_STOCK_CANCEL);
10499                 }
10500                 g_mutex_unlock(compose->mutex);
10501                 switch (val) {
10502                 case G_ALERTDEFAULT:
10503                         if (compose_can_autosave(compose) && !reedit)
10504                                 compose_remove_draft(compose);
10505                         break;
10506                 case G_ALERTALTERNATE:
10507                         compose_draft(data, COMPOSE_QUIT_EDITING);
10508                         return;
10509                 default:
10510                         return;
10511                 }
10512         }
10513
10514         compose_close(compose);
10515 }
10516
10517 static void compose_print_cb(GtkAction *action, gpointer data)
10518 {
10519         Compose *compose = (Compose *) data;
10520
10521         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10522         if (compose->targetinfo)
10523                 messageview_print(compose->targetinfo, FALSE, -1, -1, 0);
10524 }
10525
10526 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
10527 {
10528         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
10529         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
10530         Compose *compose = (Compose *) data;
10531
10532         if (active)
10533                 compose->out_encoding = (CharSet)value;
10534 }
10535
10536 static void compose_address_cb(GtkAction *action, gpointer data)
10537 {
10538         Compose *compose = (Compose *)data;
10539
10540 #ifndef USE_ALT_ADDRBOOK
10541         addressbook_open(compose);
10542 #else
10543         GError* error = NULL;
10544         addressbook_connect_signals(compose);
10545         addressbook_dbus_open(TRUE, &error);
10546         if (error) {
10547                 g_warning("%s", error->message);
10548                 g_error_free(error);
10549         }
10550 #endif
10551 }
10552
10553 static void about_show_cb(GtkAction *action, gpointer data)
10554 {
10555         about_show();
10556 }
10557
10558 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
10559 {
10560         Compose *compose = (Compose *)data;
10561         Template *tmpl;
10562         gchar *msg;
10563         AlertValue val;
10564
10565         tmpl = g_object_get_data(G_OBJECT(widget), "template");
10566         cm_return_if_fail(tmpl != NULL);
10567
10568         msg = g_strdup_printf(_("Do you want to apply the template '%s'?"),
10569                               tmpl->name);
10570         val = alertpanel(_("Apply template"), msg,
10571                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
10572         g_free(msg);
10573
10574         if (val == G_ALERTDEFAULT)
10575                 compose_template_apply(compose, tmpl, TRUE);
10576         else if (val == G_ALERTALTERNATE)
10577                 compose_template_apply(compose, tmpl, FALSE);
10578 }
10579
10580 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
10581 {
10582         Compose *compose = (Compose *)data;
10583
10584 #ifdef G_OS_UNIX
10585         if (compose->exteditor_tag != -1) {
10586                 debug_print("ignoring open external editor: external editor still open\n");
10587                 return;
10588         }
10589 #endif
10590         compose_exec_ext_editor(compose);
10591 }
10592
10593 static void compose_undo_cb(GtkAction *action, gpointer data)
10594 {
10595         Compose *compose = (Compose *)data;
10596         gboolean prev_autowrap = compose->autowrap;
10597
10598         compose->autowrap = FALSE;
10599         undo_undo(compose->undostruct);
10600         compose->autowrap = prev_autowrap;
10601 }
10602
10603 static void compose_redo_cb(GtkAction *action, gpointer data)
10604 {
10605         Compose *compose = (Compose *)data;
10606         gboolean prev_autowrap = compose->autowrap;
10607         
10608         compose->autowrap = FALSE;
10609         undo_redo(compose->undostruct);
10610         compose->autowrap = prev_autowrap;
10611 }
10612
10613 static void entry_cut_clipboard(GtkWidget *entry)
10614 {
10615         if (GTK_IS_EDITABLE(entry))
10616                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
10617         else if (GTK_IS_TEXT_VIEW(entry))
10618                 gtk_text_buffer_cut_clipboard(
10619                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10620                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
10621                         TRUE);
10622 }
10623
10624 static void entry_copy_clipboard(GtkWidget *entry)
10625 {
10626         if (GTK_IS_EDITABLE(entry))
10627                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
10628         else if (GTK_IS_TEXT_VIEW(entry))
10629                 gtk_text_buffer_copy_clipboard(
10630                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
10631                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
10632 }
10633
10634 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
10635                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
10636 {
10637         if (GTK_IS_TEXT_VIEW(entry)) {
10638                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10639                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
10640                 GtkTextIter start_iter, end_iter;
10641                 gint start, end;
10642                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
10643
10644                 if (contents == NULL)
10645                         return;
10646         
10647                 /* we shouldn't delete the selection when middle-click-pasting, or we
10648                  * can't mid-click-paste our own selection */
10649                 if (clip != GDK_SELECTION_PRIMARY) {
10650                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
10651                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
10652                 }
10653                 
10654                 if (insert_place == NULL) {
10655                         /* if insert_place isn't specified, insert at the cursor.
10656                          * used for Ctrl-V pasting */
10657                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10658                         start = gtk_text_iter_get_offset(&start_iter);
10659                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
10660                 } else {
10661                         /* if insert_place is specified, paste here.
10662                          * used for mid-click-pasting */
10663                         start = gtk_text_iter_get_offset(insert_place);
10664                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
10665                         if (prefs_common.primary_paste_unselects)
10666                                 gtk_text_buffer_select_range(buffer, insert_place, insert_place);
10667                 }
10668                 
10669                 if (!wrap) {
10670                         /* paste unwrapped: mark the paste so it's not wrapped later */
10671                         end = start + strlen(contents);
10672                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
10673                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
10674                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
10675                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
10676                         /* rewrap paragraph now (after a mid-click-paste) */
10677                         mark_start = gtk_text_buffer_get_insert(buffer);
10678                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
10679                         gtk_text_iter_backward_char(&start_iter);
10680                         compose_beautify_paragraph(compose, &start_iter, TRUE);
10681                 }
10682         } else if (GTK_IS_EDITABLE(entry))
10683                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
10684
10685         compose->modified = TRUE;
10686 }
10687
10688 static void entry_allsel(GtkWidget *entry)
10689 {
10690         if (GTK_IS_EDITABLE(entry))
10691                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
10692         else if (GTK_IS_TEXT_VIEW(entry)) {
10693                 GtkTextIter startiter, enditer;
10694                 GtkTextBuffer *textbuf;
10695
10696                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
10697                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
10698                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
10699
10700                 gtk_text_buffer_move_mark_by_name(textbuf, 
10701                         "selection_bound", &startiter);
10702                 gtk_text_buffer_move_mark_by_name(textbuf, 
10703                         "insert", &enditer);
10704         }
10705 }
10706
10707 static void compose_cut_cb(GtkAction *action, gpointer data)
10708 {
10709         Compose *compose = (Compose *)data;
10710         if (compose->focused_editable 
10711 #ifndef GENERIC_UMPC
10712             && gtk_widget_has_focus(compose->focused_editable)
10713 #endif
10714             )
10715                 entry_cut_clipboard(compose->focused_editable);
10716 }
10717
10718 static void compose_copy_cb(GtkAction *action, gpointer data)
10719 {
10720         Compose *compose = (Compose *)data;
10721         if (compose->focused_editable 
10722 #ifndef GENERIC_UMPC
10723             && gtk_widget_has_focus(compose->focused_editable)
10724 #endif
10725             )
10726                 entry_copy_clipboard(compose->focused_editable);
10727 }
10728
10729 static void compose_paste_cb(GtkAction *action, gpointer data)
10730 {
10731         Compose *compose = (Compose *)data;
10732         gint prev_autowrap;
10733         GtkTextBuffer *buffer;
10734         BLOCK_WRAP();
10735         if (compose->focused_editable &&
10736 #ifndef GENERIC_UMPC
10737             gtk_widget_has_focus(compose->focused_editable)
10738 #endif
10739                 )
10740                 entry_paste_clipboard(compose, compose->focused_editable, 
10741                                 prefs_common.linewrap_pastes,
10742                                 GDK_SELECTION_CLIPBOARD, NULL);
10743         UNBLOCK_WRAP();
10744
10745 #ifdef USE_ENCHANT
10746         if (
10747 #ifndef GENERIC_UMPC
10748                 gtk_widget_has_focus(compose->text) &&
10749 #endif
10750             compose->gtkaspell && 
10751             compose->gtkaspell->check_while_typing)
10752                 gtkaspell_highlight_all(compose->gtkaspell);
10753 #endif
10754 }
10755
10756 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
10757 {
10758         Compose *compose = (Compose *)data;
10759         gint wrap_quote = prefs_common.linewrap_quote;
10760         if (compose->focused_editable 
10761 #ifndef GENERIC_UMPC
10762             && gtk_widget_has_focus(compose->focused_editable)
10763 #endif
10764             ) {
10765                 /* let text_insert() (called directly or at a later time
10766                  * after the gtk_editable_paste_clipboard) know that 
10767                  * text is to be inserted as a quotation. implemented
10768                  * by using a simple refcount... */
10769                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
10770                                                 G_OBJECT(compose->focused_editable),
10771                                                 "paste_as_quotation"));
10772                 g_object_set_data(G_OBJECT(compose->focused_editable),
10773                                     "paste_as_quotation",
10774                                     GINT_TO_POINTER(paste_as_quotation + 1));
10775                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
10776                 entry_paste_clipboard(compose, compose->focused_editable, 
10777                                 prefs_common.linewrap_pastes,
10778                                 GDK_SELECTION_CLIPBOARD, NULL);
10779                 prefs_common.linewrap_quote = wrap_quote;
10780         }
10781 }
10782
10783 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
10784 {
10785         Compose *compose = (Compose *)data;
10786         gint prev_autowrap;
10787         GtkTextBuffer *buffer;
10788         BLOCK_WRAP();
10789         if (compose->focused_editable 
10790 #ifndef GENERIC_UMPC
10791             && gtk_widget_has_focus(compose->focused_editable)
10792 #endif
10793             )
10794                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
10795                         GDK_SELECTION_CLIPBOARD, NULL);
10796         UNBLOCK_WRAP();
10797
10798 #ifdef USE_ENCHANT
10799         if (
10800 #ifndef GENERIC_UMPC
10801                 gtk_widget_has_focus(compose->text) &&
10802 #endif
10803             compose->gtkaspell && 
10804             compose->gtkaspell->check_while_typing)
10805                 gtkaspell_highlight_all(compose->gtkaspell);
10806 #endif
10807 }
10808
10809 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
10810 {
10811         Compose *compose = (Compose *)data;
10812         gint prev_autowrap;
10813         GtkTextBuffer *buffer;
10814         BLOCK_WRAP();
10815         if (compose->focused_editable 
10816 #ifndef GENERIC_UMPC
10817             && gtk_widget_has_focus(compose->focused_editable)
10818 #endif
10819             )
10820                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
10821                         GDK_SELECTION_CLIPBOARD, NULL);
10822         UNBLOCK_WRAP();
10823
10824 #ifdef USE_ENCHANT
10825         if (
10826 #ifndef GENERIC_UMPC
10827                 gtk_widget_has_focus(compose->text) &&
10828 #endif
10829             compose->gtkaspell &&
10830             compose->gtkaspell->check_while_typing)
10831                 gtkaspell_highlight_all(compose->gtkaspell);
10832 #endif
10833 }
10834
10835 static void compose_allsel_cb(GtkAction *action, gpointer data)
10836 {
10837         Compose *compose = (Compose *)data;
10838         if (compose->focused_editable 
10839 #ifndef GENERIC_UMPC
10840             && gtk_widget_has_focus(compose->focused_editable)
10841 #endif
10842             )
10843                 entry_allsel(compose->focused_editable);
10844 }
10845
10846 static void textview_move_beginning_of_line (GtkTextView *text)
10847 {
10848         GtkTextBuffer *buffer;
10849         GtkTextMark *mark;
10850         GtkTextIter ins;
10851
10852         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10853
10854         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10855         mark = gtk_text_buffer_get_insert(buffer);
10856         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10857         gtk_text_iter_set_line_offset(&ins, 0);
10858         gtk_text_buffer_place_cursor(buffer, &ins);
10859 }
10860
10861 static void textview_move_forward_character (GtkTextView *text)
10862 {
10863         GtkTextBuffer *buffer;
10864         GtkTextMark *mark;
10865         GtkTextIter ins;
10866
10867         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10868
10869         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10870         mark = gtk_text_buffer_get_insert(buffer);
10871         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10872         if (gtk_text_iter_forward_cursor_position(&ins))
10873                 gtk_text_buffer_place_cursor(buffer, &ins);
10874 }
10875
10876 static void textview_move_backward_character (GtkTextView *text)
10877 {
10878         GtkTextBuffer *buffer;
10879         GtkTextMark *mark;
10880         GtkTextIter ins;
10881
10882         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10883
10884         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10885         mark = gtk_text_buffer_get_insert(buffer);
10886         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10887         if (gtk_text_iter_backward_cursor_position(&ins))
10888                 gtk_text_buffer_place_cursor(buffer, &ins);
10889 }
10890
10891 static void textview_move_forward_word (GtkTextView *text)
10892 {
10893         GtkTextBuffer *buffer;
10894         GtkTextMark *mark;
10895         GtkTextIter ins;
10896         gint count;
10897
10898         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10899
10900         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10901         mark = gtk_text_buffer_get_insert(buffer);
10902         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10903         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
10904         if (gtk_text_iter_forward_word_ends(&ins, count)) {
10905                 gtk_text_iter_backward_word_start(&ins);
10906                 gtk_text_buffer_place_cursor(buffer, &ins);
10907         }
10908 }
10909
10910 static void textview_move_backward_word (GtkTextView *text)
10911 {
10912         GtkTextBuffer *buffer;
10913         GtkTextMark *mark;
10914         GtkTextIter ins;
10915
10916         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10917
10918         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10919         mark = gtk_text_buffer_get_insert(buffer);
10920         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10921         if (gtk_text_iter_backward_word_starts(&ins, 1))
10922                 gtk_text_buffer_place_cursor(buffer, &ins);
10923 }
10924
10925 static void textview_move_end_of_line (GtkTextView *text)
10926 {
10927         GtkTextBuffer *buffer;
10928         GtkTextMark *mark;
10929         GtkTextIter ins;
10930
10931         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10932
10933         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10934         mark = gtk_text_buffer_get_insert(buffer);
10935         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10936         if (gtk_text_iter_forward_to_line_end(&ins))
10937                 gtk_text_buffer_place_cursor(buffer, &ins);
10938 }
10939
10940 static void textview_move_next_line (GtkTextView *text)
10941 {
10942         GtkTextBuffer *buffer;
10943         GtkTextMark *mark;
10944         GtkTextIter ins;
10945         gint offset;
10946
10947         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10948
10949         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10950         mark = gtk_text_buffer_get_insert(buffer);
10951         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10952         offset = gtk_text_iter_get_line_offset(&ins);
10953         if (gtk_text_iter_forward_line(&ins)) {
10954                 gtk_text_iter_set_line_offset(&ins, offset);
10955                 gtk_text_buffer_place_cursor(buffer, &ins);
10956         }
10957 }
10958
10959 static void textview_move_previous_line (GtkTextView *text)
10960 {
10961         GtkTextBuffer *buffer;
10962         GtkTextMark *mark;
10963         GtkTextIter ins;
10964         gint offset;
10965
10966         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10967
10968         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10969         mark = gtk_text_buffer_get_insert(buffer);
10970         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10971         offset = gtk_text_iter_get_line_offset(&ins);
10972         if (gtk_text_iter_backward_line(&ins)) {
10973                 gtk_text_iter_set_line_offset(&ins, offset);
10974                 gtk_text_buffer_place_cursor(buffer, &ins);
10975         }
10976 }
10977
10978 static void textview_delete_forward_character (GtkTextView *text)
10979 {
10980         GtkTextBuffer *buffer;
10981         GtkTextMark *mark;
10982         GtkTextIter ins, end_iter;
10983
10984         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
10985
10986         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
10987         mark = gtk_text_buffer_get_insert(buffer);
10988         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
10989         end_iter = ins;
10990         if (gtk_text_iter_forward_char(&end_iter)) {
10991                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
10992         }
10993 }
10994
10995 static void textview_delete_backward_character (GtkTextView *text)
10996 {
10997         GtkTextBuffer *buffer;
10998         GtkTextMark *mark;
10999         GtkTextIter ins, end_iter;
11000
11001         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11002
11003         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11004         mark = gtk_text_buffer_get_insert(buffer);
11005         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11006         end_iter = ins;
11007         if (gtk_text_iter_backward_char(&end_iter)) {
11008                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
11009         }
11010 }
11011
11012 static void textview_delete_forward_word (GtkTextView *text)
11013 {
11014         GtkTextBuffer *buffer;
11015         GtkTextMark *mark;
11016         GtkTextIter ins, end_iter;
11017
11018         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11019
11020         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11021         mark = gtk_text_buffer_get_insert(buffer);
11022         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11023         end_iter = ins;
11024         if (gtk_text_iter_forward_word_end(&end_iter)) {
11025                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
11026         }
11027 }
11028
11029 static void textview_delete_backward_word (GtkTextView *text)
11030 {
11031         GtkTextBuffer *buffer;
11032         GtkTextMark *mark;
11033         GtkTextIter ins, end_iter;
11034
11035         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11036
11037         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11038         mark = gtk_text_buffer_get_insert(buffer);
11039         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11040         end_iter = ins;
11041         if (gtk_text_iter_backward_word_start(&end_iter)) {
11042                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
11043         }
11044 }
11045
11046 static void textview_delete_line (GtkTextView *text)
11047 {
11048         GtkTextBuffer *buffer;
11049         GtkTextMark *mark;
11050         GtkTextIter ins, start_iter, end_iter;
11051
11052         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11053
11054         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11055         mark = gtk_text_buffer_get_insert(buffer);
11056         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11057
11058         start_iter = ins;
11059         gtk_text_iter_set_line_offset(&start_iter, 0);
11060
11061         end_iter = ins;
11062         if (gtk_text_iter_ends_line(&end_iter)){
11063                 if (!gtk_text_iter_forward_char(&end_iter))
11064                         gtk_text_iter_backward_char(&start_iter);
11065         }
11066         else 
11067                 gtk_text_iter_forward_to_line_end(&end_iter);
11068         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
11069 }
11070
11071 static void textview_delete_to_line_end (GtkTextView *text)
11072 {
11073         GtkTextBuffer *buffer;
11074         GtkTextMark *mark;
11075         GtkTextIter ins, end_iter;
11076
11077         cm_return_if_fail(GTK_IS_TEXT_VIEW(text));
11078
11079         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
11080         mark = gtk_text_buffer_get_insert(buffer);
11081         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
11082         end_iter = ins;
11083         if (gtk_text_iter_ends_line(&end_iter))
11084                 gtk_text_iter_forward_char(&end_iter);
11085         else
11086                 gtk_text_iter_forward_to_line_end(&end_iter);
11087         gtk_text_buffer_delete(buffer, &ins, &end_iter);
11088 }
11089
11090 #define DO_ACTION(name, act) {                                          \
11091         if(!strcmp(name, a_name)) {                                     \
11092                 return act;                                             \
11093         }                                                               \
11094 }
11095 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
11096 {
11097         const gchar *a_name = gtk_action_get_name(action);
11098         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
11099         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
11100         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
11101         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
11102         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
11103         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
11104         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
11105         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
11106         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
11107         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
11108         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
11109         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
11110         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
11111         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
11112         return COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED;
11113 }
11114
11115 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
11116 {
11117         Compose *compose = (Compose *)data;
11118         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
11119         ComposeCallAdvancedAction action = COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED;
11120         
11121         action = compose_call_advanced_action_from_path(gaction);
11122
11123         static struct {
11124                 void (*do_action) (GtkTextView *text);
11125         } action_table[] = {
11126                 {textview_move_beginning_of_line},
11127                 {textview_move_forward_character},
11128                 {textview_move_backward_character},
11129                 {textview_move_forward_word},
11130                 {textview_move_backward_word},
11131                 {textview_move_end_of_line},
11132                 {textview_move_next_line},
11133                 {textview_move_previous_line},
11134                 {textview_delete_forward_character},
11135                 {textview_delete_backward_character},
11136                 {textview_delete_forward_word},
11137                 {textview_delete_backward_word},
11138                 {textview_delete_line},
11139                 {textview_delete_to_line_end}
11140         };
11141
11142         if (!gtk_widget_has_focus(GTK_WIDGET(text))) return;
11143
11144         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
11145             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
11146                 if (action_table[action].do_action)
11147                         action_table[action].do_action(text);
11148                 else
11149                         g_warning("Not implemented yet.");
11150         }
11151 }
11152
11153 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
11154 {
11155         GtkAllocation allocation;
11156         GtkWidget *parent;
11157         gchar *str = NULL;
11158         
11159         if (GTK_IS_EDITABLE(widget)) {
11160                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
11161                 gtk_editable_set_position(GTK_EDITABLE(widget), 
11162                         strlen(str));
11163                 g_free(str);
11164                 if ((parent = gtk_widget_get_parent(widget))
11165                  && (parent = gtk_widget_get_parent(parent))
11166                  && (parent = gtk_widget_get_parent(parent))) {
11167                         if (GTK_IS_SCROLLED_WINDOW(parent)) {
11168                                 gtk_widget_get_allocation(widget, &allocation);
11169                                 gint y = allocation.y;
11170                                 gint height = allocation.height;
11171                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
11172                                         (GTK_SCROLLED_WINDOW(parent));
11173
11174                                 gfloat value = gtk_adjustment_get_value(shown);
11175                                 gfloat upper = gtk_adjustment_get_upper(shown);
11176                                 gfloat page_size = gtk_adjustment_get_page_size(shown);
11177                                 if (y < (int)value) {
11178                                         gtk_adjustment_set_value(shown, y - 1);
11179                                 }
11180                                 if ((y + height) > ((int)value + (int)page_size)) {
11181                                         if ((y - height - 1) < ((int)upper - (int)page_size)) {
11182                                                 gtk_adjustment_set_value(shown, 
11183                                                         y + height - (int)page_size - 1);
11184                                         } else {
11185                                                 gtk_adjustment_set_value(shown, 
11186                                                         (int)upper - (int)page_size - 1);
11187                                         }
11188                                 }
11189                         }
11190                 }
11191         }
11192
11193         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
11194                 compose->focused_editable = widget;
11195         
11196 #ifdef GENERIC_UMPC
11197         if (GTK_IS_TEXT_VIEW(widget) 
11198             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
11199                 g_object_ref(compose->notebook);
11200                 g_object_ref(compose->edit_vbox);
11201                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
11202                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
11203                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
11204                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
11205                 g_object_unref(compose->notebook);
11206                 g_object_unref(compose->edit_vbox);
11207                 g_signal_handlers_block_by_func(G_OBJECT(widget),
11208                                         G_CALLBACK(compose_grab_focus_cb),
11209                                         compose);
11210                 gtk_widget_grab_focus(widget);
11211                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
11212                                         G_CALLBACK(compose_grab_focus_cb),
11213                                         compose);
11214         } else if (!GTK_IS_TEXT_VIEW(widget) 
11215                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
11216                 g_object_ref(compose->notebook);
11217                 g_object_ref(compose->edit_vbox);
11218                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
11219                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
11220                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
11221                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
11222                 g_object_unref(compose->notebook);
11223                 g_object_unref(compose->edit_vbox);
11224                 g_signal_handlers_block_by_func(G_OBJECT(widget),
11225                                         G_CALLBACK(compose_grab_focus_cb),
11226                                         compose);
11227                 gtk_widget_grab_focus(widget);
11228                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
11229                                         G_CALLBACK(compose_grab_focus_cb),
11230                                         compose);
11231         }
11232 #endif
11233 }
11234
11235 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
11236 {
11237         compose->modified = TRUE;
11238 /*      compose_beautify_paragraph(compose, NULL, TRUE); */
11239 #ifndef GENERIC_UMPC
11240         compose_set_title(compose);
11241 #endif
11242 }
11243
11244 static void compose_wrap_cb(GtkAction *action, gpointer data)
11245 {
11246         Compose *compose = (Compose *)data;
11247         compose_beautify_paragraph(compose, NULL, TRUE);
11248 }
11249
11250 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
11251 {
11252         Compose *compose = (Compose *)data;
11253         compose_wrap_all_full(compose, TRUE);
11254 }
11255
11256 static void compose_find_cb(GtkAction *action, gpointer data)
11257 {
11258         Compose *compose = (Compose *)data;
11259
11260         message_search_compose(compose);
11261 }
11262
11263 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
11264                                          gpointer        data)
11265 {
11266         Compose *compose = (Compose *)data;
11267         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11268         if (compose->autowrap)
11269                 compose_wrap_all_full(compose, TRUE);
11270         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11271 }
11272
11273 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
11274                                          gpointer        data)
11275 {
11276         Compose *compose = (Compose *)data;
11277         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11278 }
11279
11280 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
11281 {
11282         Compose *compose = (Compose *)data;
11283
11284         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11285 }
11286
11287 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
11288 {
11289         Compose *compose = (Compose *)data;
11290
11291         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
11292 }
11293
11294 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
11295 {
11296         g_free(compose->privacy_system);
11297         g_free(compose->encdata);
11298
11299         compose->privacy_system = g_strdup(account->default_privacy_system);
11300         compose_update_privacy_system_menu_item(compose, warn);
11301 }
11302
11303 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
11304 {
11305         Compose *compose = (Compose *)data;
11306
11307         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
11308                 gtk_widget_show(compose->ruler_hbox);
11309                 prefs_common.show_ruler = TRUE;
11310         } else {
11311                 gtk_widget_hide(compose->ruler_hbox);
11312                 gtk_widget_queue_resize(compose->edit_vbox);
11313                 prefs_common.show_ruler = FALSE;
11314         }
11315 }
11316
11317 static void compose_attach_drag_received_cb (GtkWidget          *widget,
11318                                              GdkDragContext     *context,
11319                                              gint                x,
11320                                              gint                y,
11321                                              GtkSelectionData   *data,
11322                                              guint               info,
11323                                              guint               time,
11324                                              gpointer            user_data)
11325 {
11326         Compose *compose = (Compose *)user_data;
11327         GList *list, *tmp;
11328         GdkAtom type;
11329
11330         type = gtk_selection_data_get_data_type(data);
11331         if ((gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list"))
11332            && gtk_drag_get_source_widget(context) !=
11333                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11334                 list = uri_list_extract_filenames(
11335                         (const gchar *)gtk_selection_data_get_data(data));
11336                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11337                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
11338                         compose_attach_append
11339                                 (compose, (const gchar *)tmp->data,
11340                                  utf8_filename, NULL, NULL);
11341                         g_free(utf8_filename);
11342                 }
11343                 if (list) compose_changed_cb(NULL, compose);
11344                 list_free_strings(list);
11345                 g_list_free(list);
11346         } else if (gtk_drag_get_source_widget(context) 
11347                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
11348                 /* comes from our summaryview */
11349                 SummaryView * summaryview = NULL;
11350                 GSList * list = NULL, *cur = NULL;
11351                 
11352                 if (mainwindow_get_mainwindow())
11353                         summaryview = mainwindow_get_mainwindow()->summaryview;
11354                 
11355                 if (summaryview)
11356                         list = summary_get_selected_msg_list(summaryview);
11357                 
11358                 for (cur = list; cur; cur = cur->next) {
11359                         MsgInfo *msginfo = (MsgInfo *)cur->data;
11360                         gchar *file = NULL;
11361                         if (msginfo)
11362                                 file = procmsg_get_message_file_full(msginfo, 
11363                                         TRUE, TRUE);
11364                         if (file) {
11365                                 compose_attach_append(compose, (const gchar *)file, 
11366                                         (const gchar *)file, "message/rfc822", NULL);
11367                                 g_free(file);
11368                         }
11369                 }
11370                 g_slist_free(list);
11371         }
11372 }
11373
11374 static gboolean compose_drag_drop(GtkWidget *widget,
11375                                   GdkDragContext *drag_context,
11376                                   gint x, gint y,
11377                                   guint time, gpointer user_data)
11378 {
11379         /* not handling this signal makes compose_insert_drag_received_cb
11380          * called twice */
11381         return TRUE;                                     
11382 }
11383
11384 static gboolean completion_set_focus_to_subject
11385                                         (GtkWidget    *widget,
11386                                          GdkEventKey  *event,
11387                                          Compose      *compose)
11388 {
11389         cm_return_val_if_fail(compose != NULL, FALSE);
11390
11391         /* make backtab move to subject field */
11392         if(event->keyval == GDK_KEY_ISO_Left_Tab) {
11393                 gtk_widget_grab_focus(compose->subject_entry);
11394                 return TRUE;
11395         }
11396         return FALSE;
11397 }
11398
11399 static void compose_insert_drag_received_cb (GtkWidget          *widget,
11400                                              GdkDragContext     *drag_context,
11401                                              gint                x,
11402                                              gint                y,
11403                                              GtkSelectionData   *data,
11404                                              guint               info,
11405                                              guint               time,
11406                                              gpointer            user_data)
11407 {
11408         Compose *compose = (Compose *)user_data;
11409         GList *list, *tmp;
11410         GdkAtom type;
11411         guint num_files;
11412         gchar *msg;
11413
11414         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
11415          * does not work */
11416         type = gtk_selection_data_get_data_type(data);
11417         if (gdk_atom_name(type) && !strcmp(gdk_atom_name(type), "text/uri-list")) {
11418                 AlertValue val = G_ALERTDEFAULT;
11419                 const gchar* ddata = (const gchar *)gtk_selection_data_get_data(data);
11420
11421                 list = uri_list_extract_filenames(ddata);
11422                 num_files = g_list_length(list);
11423                 if (list == NULL && strstr(ddata, "://")) {
11424                         /* Assume a list of no files, and data has ://, is a remote link */
11425                         gchar *tmpdata = g_strstrip(g_strdup(ddata));
11426                         gchar *tmpfile = get_tmp_file();
11427                         str_write_to_file(tmpdata, tmpfile);
11428                         g_free(tmpdata);  
11429                         compose_insert_file(compose, tmpfile);
11430                         claws_unlink(tmpfile);
11431                         g_free(tmpfile);
11432                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11433                         compose_beautify_paragraph(compose, NULL, TRUE);
11434                         return;
11435                 }
11436                 switch (prefs_common.compose_dnd_mode) {
11437                         case COMPOSE_DND_ASK:
11438                                 msg = g_strdup_printf(
11439                                                 ngettext(
11440                                                         "Do you want to insert the contents of the file "
11441                                                         "into the message body, or attach it to the email?",
11442                                                         "Do you want to insert the contents of the %d files "
11443                                                         "into the message body, or attach them to the email?",
11444                                                         num_files),
11445                                                 num_files);
11446                                 val = alertpanel_full(_("Insert or attach?"), msg,
11447                                           GTK_STOCK_CANCEL, g_strconcat("+", _("_Insert"), NULL), _("_Attach"),
11448                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
11449                                 g_free(msg);
11450                                 break;
11451                         case COMPOSE_DND_INSERT:
11452                                 val = G_ALERTALTERNATE;
11453                                 break;
11454                         case COMPOSE_DND_ATTACH:
11455                                 val = G_ALERTOTHER;
11456                                 break;
11457                         default:
11458                                 /* unexpected case */
11459                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
11460                 }
11461
11462                 if (val & G_ALERTDISABLE) {
11463                         val &= ~G_ALERTDISABLE;
11464                         /* remember what action to perform by default, only if we don't click Cancel */
11465                         if (val == G_ALERTALTERNATE)
11466                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
11467                         else if (val == G_ALERTOTHER)
11468                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
11469                 }
11470
11471                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
11472                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
11473                         list_free_strings(list);
11474                         g_list_free(list);
11475                         return;
11476                 } else if (val == G_ALERTOTHER) {
11477                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
11478                         list_free_strings(list);
11479                         g_list_free(list);
11480                         return;
11481                 } 
11482
11483                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
11484                         compose_insert_file(compose, (const gchar *)tmp->data);
11485                 }
11486                 list_free_strings(list);
11487                 g_list_free(list);
11488                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11489                 return;
11490         }
11491 }
11492
11493 static void compose_header_drag_received_cb (GtkWidget          *widget,
11494                                              GdkDragContext     *drag_context,
11495                                              gint                x,
11496                                              gint                y,
11497                                              GtkSelectionData   *data,
11498                                              guint               info,
11499                                              guint               time,
11500                                              gpointer            user_data)
11501 {
11502         GtkEditable *entry = (GtkEditable *)user_data;
11503         const gchar *email = (const gchar *)gtk_selection_data_get_data(data);
11504
11505         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
11506          * does not work */
11507
11508         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
11509                 gchar *decoded=g_new(gchar, strlen(email));
11510                 int start = 0;
11511
11512                 decode_uri(decoded, email + strlen("mailto:")); /* will fit */
11513                 gtk_editable_delete_text(entry, 0, -1);
11514                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
11515                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
11516                 g_free(decoded);
11517                 return;
11518         }
11519         gtk_drag_finish(drag_context, TRUE, FALSE, time);
11520 }
11521
11522 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
11523 {
11524         Compose *compose = (Compose *)data;
11525
11526         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11527                 compose->return_receipt = TRUE;
11528         else
11529                 compose->return_receipt = FALSE;
11530 }
11531
11532 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
11533 {
11534         Compose *compose = (Compose *)data;
11535
11536         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
11537                 compose->remove_references = TRUE;
11538         else
11539                 compose->remove_references = FALSE;
11540 }
11541
11542 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
11543                                         ComposeHeaderEntry *headerentry)
11544 {
11545         gtk_entry_set_text(GTK_ENTRY(headerentry->entry), "");
11546         return FALSE;
11547 }
11548
11549 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
11550                                             GdkEventKey *event,
11551                                             ComposeHeaderEntry *headerentry)
11552 {
11553         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
11554             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
11555             !(event->state & GDK_MODIFIER_MASK) &&
11556             (event->keyval == GDK_KEY_BackSpace) &&
11557             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
11558                 gtk_container_remove
11559                         (GTK_CONTAINER(headerentry->compose->header_table),
11560                          headerentry->combo);
11561                 gtk_container_remove
11562                         (GTK_CONTAINER(headerentry->compose->header_table),
11563                          headerentry->entry);
11564                 headerentry->compose->header_list =
11565                         g_slist_remove(headerentry->compose->header_list,
11566                                        headerentry);
11567                 g_free(headerentry);
11568         } else  if (event->keyval == GDK_KEY_Tab) {
11569                 if (headerentry->compose->header_last == headerentry) {
11570                         /* Override default next focus, and give it to subject_entry
11571                          * instead of notebook tabs
11572                          */
11573                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
11574                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
11575                         return TRUE;
11576                 }
11577         }
11578         return FALSE;
11579 }
11580
11581 static gboolean scroll_postpone(gpointer data)
11582 {
11583         Compose *compose = (Compose *)data;
11584
11585         if (compose->batch)
11586                 return FALSE;
11587
11588         GTK_EVENTS_FLUSH();
11589         compose_show_first_last_header(compose, FALSE);
11590         return FALSE;
11591 }
11592
11593 static void compose_headerentry_changed_cb(GtkWidget *entry,
11594                                     ComposeHeaderEntry *headerentry)
11595 {
11596         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
11597                 compose_create_header_entry(headerentry->compose);
11598                 g_signal_handlers_disconnect_matched
11599                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
11600                          0, 0, NULL, NULL, headerentry);
11601
11602                 if (!headerentry->compose->batch)
11603                         g_timeout_add(0, scroll_postpone, headerentry->compose);
11604         }
11605 }
11606
11607 static gboolean compose_defer_auto_save_draft(Compose *compose)
11608 {
11609         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
11610         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
11611         return FALSE;
11612 }
11613
11614 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
11615 {
11616         GtkAdjustment *vadj;
11617
11618         cm_return_if_fail(compose);
11619
11620         if(compose->batch)
11621                 return;
11622
11623         cm_return_if_fail(GTK_IS_WIDGET(compose->header_table));
11624         cm_return_if_fail(GTK_IS_VIEWPORT(gtk_widget_get_parent(compose->header_table)));
11625         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(
11626                                 gtk_widget_get_parent(compose->header_table)));
11627         gtk_adjustment_set_value(vadj, (show_first ?
11628                                 gtk_adjustment_get_lower(vadj) :
11629                                 (gtk_adjustment_get_upper(vadj) -
11630                                 gtk_adjustment_get_page_size(vadj))));
11631         gtk_adjustment_changed(vadj);
11632 }
11633
11634 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
11635                           const gchar *text, gint len, Compose *compose)
11636 {
11637         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
11638                                 (G_OBJECT(compose->text), "paste_as_quotation"));
11639         GtkTextMark *mark;
11640
11641         cm_return_if_fail(text != NULL);
11642
11643         g_signal_handlers_block_by_func(G_OBJECT(buffer),
11644                                         G_CALLBACK(text_inserted),
11645                                         compose);
11646         if (paste_as_quotation) {
11647                 gchar *new_text;
11648                 const gchar *qmark;
11649                 guint pos = 0;
11650                 GtkTextIter start_iter;
11651
11652                 if (len < 0)
11653                         len = strlen(text);
11654
11655                 new_text = g_strndup(text, len);
11656
11657                 qmark = compose_quote_char_from_context(compose);
11658
11659                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11660                 gtk_text_buffer_place_cursor(buffer, iter);
11661
11662                 pos = gtk_text_iter_get_offset(iter);
11663
11664                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
11665                                                   _("Quote format error at line %d."));
11666                 quote_fmt_reset_vartable();
11667                 g_free(new_text);
11668                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
11669                                   GINT_TO_POINTER(paste_as_quotation - 1));
11670                                   
11671                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11672                 gtk_text_buffer_place_cursor(buffer, iter);
11673                 gtk_text_buffer_delete_mark(buffer, mark);
11674
11675                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
11676                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
11677                 compose_beautify_paragraph(compose, &start_iter, FALSE);
11678                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
11679                 gtk_text_buffer_delete_mark(buffer, mark);
11680         } else {
11681                 if (strcmp(text, "\n") || compose->automatic_break
11682                 || gtk_text_iter_starts_line(iter)) {
11683                         GtkTextIter before_ins;
11684                         gtk_text_buffer_insert(buffer, iter, text, len);
11685                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
11686                                 before_ins = *iter; 
11687                                 gtk_text_iter_backward_chars(&before_ins, len);
11688                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
11689                         }
11690                 } else {
11691                         /* check if the preceding is just whitespace or quote */
11692                         GtkTextIter start_line;
11693                         gchar *tmp = NULL, *quote = NULL;
11694                         gint quote_len = 0, is_normal = 0;
11695                         start_line = *iter;
11696                         gtk_text_iter_set_line_offset(&start_line, 0); 
11697                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
11698                         g_strstrip(tmp);
11699
11700                         if (*tmp == '\0') {
11701                                 is_normal = 1;
11702                         } else {
11703                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
11704                                 if (quote)
11705                                         is_normal = 1;
11706                                 g_free(quote);
11707                         }
11708                         g_free(tmp);
11709                         
11710                         if (is_normal) {
11711                                 gtk_text_buffer_insert(buffer, iter, text, len);
11712                         } else {
11713                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
11714                                         iter, text, len, "no_join", NULL);
11715                         }
11716                 }
11717         }
11718         
11719         if (!paste_as_quotation) {
11720                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
11721                 compose_beautify_paragraph(compose, iter, FALSE);
11722                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
11723                 gtk_text_buffer_delete_mark(buffer, mark);
11724         }
11725
11726         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
11727                                           G_CALLBACK(text_inserted),
11728                                           compose);
11729         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
11730
11731         if (compose_can_autosave(compose) && 
11732             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
11733             compose->draft_timeout_tag != COMPOSE_DRAFT_TIMEOUT_FORBIDDEN /* disabled while loading */)
11734                 compose->draft_timeout_tag = g_timeout_add
11735                         (500, (GSourceFunc) compose_defer_auto_save_draft, compose);
11736 }
11737
11738 #if USE_ENCHANT
11739 static void compose_check_all(GtkAction *action, gpointer data)
11740 {
11741         Compose *compose = (Compose *)data;
11742         if (!compose->gtkaspell)
11743                 return;
11744                 
11745         if (gtk_widget_has_focus(compose->subject_entry))
11746                 claws_spell_entry_check_all(
11747                         CLAWS_SPELL_ENTRY(compose->subject_entry));             
11748         else
11749                 gtkaspell_check_all(compose->gtkaspell);
11750 }
11751
11752 static void compose_highlight_all(GtkAction *action, gpointer data)
11753 {
11754         Compose *compose = (Compose *)data;
11755         if (compose->gtkaspell) {
11756                 claws_spell_entry_recheck_all(
11757                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11758                 gtkaspell_highlight_all(compose->gtkaspell);
11759         }
11760 }
11761
11762 static void compose_check_backwards(GtkAction *action, gpointer data)
11763 {
11764         Compose *compose = (Compose *)data;
11765         if (!compose->gtkaspell) {
11766                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11767                 return;
11768         }
11769
11770         if (gtk_widget_has_focus(compose->subject_entry))
11771                 claws_spell_entry_check_backwards(
11772                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11773         else
11774                 gtkaspell_check_backwards(compose->gtkaspell);
11775 }
11776
11777 static void compose_check_forwards_go(GtkAction *action, gpointer data)
11778 {
11779         Compose *compose = (Compose *)data;
11780         if (!compose->gtkaspell) {
11781                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
11782                 return;
11783         }
11784
11785         if (gtk_widget_has_focus(compose->subject_entry))
11786                 claws_spell_entry_check_forwards_go(
11787                         CLAWS_SPELL_ENTRY(compose->subject_entry));
11788         else
11789                 gtkaspell_check_forwards_go(compose->gtkaspell);
11790 }
11791 #endif
11792
11793 /*!
11794  *\brief        Guess originating forward account from MsgInfo and several 
11795  *              "common preference" settings. Return NULL if no guess. 
11796  */
11797 static PrefsAccount *compose_find_account(MsgInfo *msginfo)
11798 {
11799         PrefsAccount *account = NULL;
11800         
11801         cm_return_val_if_fail(msginfo, NULL);
11802         cm_return_val_if_fail(msginfo->folder, NULL);
11803         cm_return_val_if_fail(msginfo->folder->prefs, NULL);
11804
11805         if (msginfo->folder->prefs->enable_default_account)
11806                 account = account_find_from_id(msginfo->folder->prefs->default_account);
11807                 
11808         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
11809                 gchar *to;
11810                 Xstrdup_a(to, msginfo->to, return NULL);
11811                 extract_address(to);
11812                 account = account_find_from_address(to, FALSE);
11813         }
11814
11815         if (!account && prefs_common.forward_account_autosel) {
11816                 gchar cc[BUFFSIZE];
11817                 if (!procheader_get_header_from_msginfo
11818                         (msginfo, cc,sizeof cc , "Cc:")) { 
11819                         gchar *buf = cc + strlen("Cc:");
11820                         extract_address(buf);
11821                         account = account_find_from_address(buf, FALSE);
11822                 }
11823         }
11824         
11825         if (!account && prefs_common.forward_account_autosel) {
11826                 gchar deliveredto[BUFFSIZE];
11827                 if (!procheader_get_header_from_msginfo
11828                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
11829                         gchar *buf = deliveredto + strlen("Delivered-To:");
11830                         extract_address(buf);
11831                         account = account_find_from_address(buf, FALSE);
11832                 }
11833         }
11834
11835         if (!account)
11836                 account = msginfo->folder->folder->account;
11837         
11838         return account;
11839 }
11840
11841 gboolean compose_close(Compose *compose)
11842 {
11843         gint x, y;
11844
11845         cm_return_val_if_fail(compose, FALSE);
11846
11847         if (!g_mutex_trylock(compose->mutex)) {
11848                 /* we have to wait for the (possibly deferred by auto-save)
11849                  * drafting to be done, before destroying the compose under
11850                  * it. */
11851                 debug_print("waiting for drafting to finish...\n");
11852                 compose_allow_user_actions(compose, FALSE);
11853                 if (compose->close_timeout_tag == 0) {
11854                         compose->close_timeout_tag = 
11855                                 g_timeout_add (500, (GSourceFunc) compose_close,
11856                                 compose);
11857                 }
11858                 return TRUE;
11859         }
11860
11861         if (compose->draft_timeout_tag >= 0) {
11862                 g_source_remove(compose->draft_timeout_tag);
11863                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;
11864         }
11865
11866         gtkut_widget_get_uposition(compose->window, &x, &y);
11867         if (!compose->batch) {
11868                 prefs_common.compose_x = x;
11869                 prefs_common.compose_y = y;
11870         }
11871         g_mutex_unlock(compose->mutex);
11872         compose_destroy(compose);
11873         return FALSE;
11874 }
11875
11876 /**
11877  * Add entry field for each address in list.
11878  * \param compose     E-Mail composition object.
11879  * \param listAddress List of (formatted) E-Mail addresses.
11880  */
11881 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
11882         GList *node;
11883         gchar *addr;
11884         node = listAddress;
11885         while( node ) {
11886                 addr = ( gchar * ) node->data;
11887                 compose_entry_append( compose, addr, COMPOSE_TO, PREF_NONE );
11888                 node = g_list_next( node );
11889         }
11890 }
11891
11892 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
11893                                     guint action, gboolean opening_multiple)
11894 {
11895         gchar *body = NULL;
11896         GSList *new_msglist = NULL;
11897         MsgInfo *tmp_msginfo = NULL;
11898         gboolean originally_enc = FALSE;
11899         gboolean originally_sig = FALSE;
11900         Compose *compose = NULL;
11901         gchar *s_system = NULL;
11902
11903         cm_return_if_fail(msgview != NULL);
11904
11905         cm_return_if_fail(msginfo_list != NULL);
11906
11907         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
11908                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
11909                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
11910
11911                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
11912                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
11913                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
11914                                                 orig_msginfo, mimeinfo);
11915                         if (tmp_msginfo != NULL) {
11916                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
11917
11918                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
11919                                 privacy_msginfo_get_signed_state(orig_msginfo, &s_system);
11920                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
11921
11922                                 tmp_msginfo->folder = orig_msginfo->folder;
11923                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
11924                                 if (orig_msginfo->tags) {
11925                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
11926                                         tmp_msginfo->folder->tags_dirty = TRUE;
11927                                 }
11928                         }
11929                 }
11930         }
11931
11932         if (!opening_multiple)
11933                 body = messageview_get_selection(msgview);
11934
11935         if (new_msglist) {
11936                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
11937                 procmsg_msginfo_free(&tmp_msginfo);
11938                 g_slist_free(new_msglist);
11939         } else
11940                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
11941
11942         if (compose && originally_enc) {
11943                 compose_force_encryption(compose, compose->account, FALSE, s_system);
11944         }
11945
11946         if (compose && originally_sig && compose->account->default_sign_reply) {
11947                 compose_force_signing(compose, compose->account, s_system);
11948         }
11949         g_free(s_system);
11950         g_free(body);
11951         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
11952 }
11953
11954 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
11955                                     guint action)
11956 {
11957         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
11958         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
11959                 GSList *cur = msginfo_list;
11960                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
11961                                                "messages. Opening the windows "
11962                                                "could take some time. Do you "
11963                                                "want to continue?"), 
11964                                                g_slist_length(msginfo_list));
11965                 if (g_slist_length(msginfo_list) > 9
11966                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
11967                     != G_ALERTALTERNATE) {
11968                         g_free(msg);
11969                         return;
11970                 }
11971                 g_free(msg);
11972                 /* We'll open multiple compose windows */
11973                 /* let the WM place the next windows */
11974                 compose_force_window_origin = FALSE;
11975                 for (; cur; cur = cur->next) {
11976                         GSList tmplist;
11977                         tmplist.data = cur->data;
11978                         tmplist.next = NULL;
11979                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
11980                 }
11981                 compose_force_window_origin = TRUE;
11982         } else {
11983                 /* forwarding multiple mails as attachments is done via a
11984                  * single compose window */
11985                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
11986         }
11987 }
11988
11989 void compose_check_for_email_account(Compose *compose)
11990 {
11991         PrefsAccount *ac = NULL, *curr = NULL;
11992         GList *list;
11993         
11994         if (!compose)
11995                 return;
11996
11997         if (compose->account && compose->account->protocol == A_NNTP) {
11998                 ac = account_get_cur_account();
11999                 if (ac->protocol == A_NNTP) {
12000                         list = account_get_list();
12001                         
12002                         for( ; list != NULL ; list = g_list_next(list)) {
12003                                 curr = (PrefsAccount *) list->data;
12004                                 if (curr->protocol != A_NNTP) {
12005                                         ac = curr;
12006                                         break;
12007                                 }
12008                         }
12009                 }
12010                 combobox_select_by_data(GTK_COMBO_BOX(compose->account_combo),
12011                                         ac->account_id); 
12012         }
12013 }
12014
12015 void compose_reply_to_address(MessageView *msgview, MsgInfo *msginfo, 
12016                                 const gchar *address)
12017 {
12018         GSList *msginfo_list = NULL;
12019         gchar *body =  messageview_get_selection(msgview);
12020         Compose *compose;
12021         
12022         msginfo_list = g_slist_prepend(msginfo_list, msginfo);
12023         
12024         compose = compose_reply_mode(COMPOSE_REPLY_TO_ADDRESS, msginfo_list, body);
12025         compose_check_for_email_account(compose);
12026         compose_set_folder_prefs(compose, msginfo->folder, FALSE);
12027         compose_entry_append(compose, address, COMPOSE_TO, PREF_NONE);
12028         compose_reply_set_subject(compose, msginfo);
12029
12030         g_free(body);
12031         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
12032 }
12033
12034 void compose_set_position(Compose *compose, gint pos)
12035 {
12036         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
12037
12038         gtkut_text_view_set_position(text, pos);
12039 }
12040
12041 gboolean compose_search_string(Compose *compose,
12042                                 const gchar *str, gboolean case_sens)
12043 {
12044         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
12045
12046         return gtkut_text_view_search_string(text, str, case_sens);
12047 }
12048
12049 gboolean compose_search_string_backward(Compose *compose,
12050                                 const gchar *str, gboolean case_sens)
12051 {
12052         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
12053
12054         return gtkut_text_view_search_string_backward(text, str, case_sens);
12055 }
12056
12057 /* allocate a msginfo structure and populate its data from a compose data structure */
12058 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
12059 {
12060         MsgInfo *newmsginfo;
12061         GSList *list;
12062         gchar buf[BUFFSIZE];
12063
12064         cm_return_val_if_fail( compose != NULL, NULL );
12065
12066         newmsginfo = procmsg_msginfo_new();
12067
12068         /* date is now */
12069         get_rfc822_date(buf, sizeof(buf));
12070         newmsginfo->date = g_strdup(buf);
12071
12072         /* from */
12073         if (compose->from_name) {
12074                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
12075                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
12076         }
12077
12078         /* subject */
12079         if (compose->subject_entry)
12080                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
12081
12082         /* to, cc, reply-to, newsgroups */
12083         for (list = compose->header_list; list; list = list->next) {
12084                 gchar *header = gtk_editable_get_chars(
12085                                                                 GTK_EDITABLE(
12086                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
12087                 gchar *entry = gtk_editable_get_chars(
12088                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
12089
12090                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
12091                         if ( newmsginfo->to == NULL ) {
12092                                 newmsginfo->to = g_strdup(entry);
12093                         } else if (entry && *entry) {
12094                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
12095                                 g_free(newmsginfo->to);
12096                                 newmsginfo->to = tmp;
12097                         }
12098                 } else
12099                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
12100                         if ( newmsginfo->cc == NULL ) {
12101                                 newmsginfo->cc = g_strdup(entry);
12102                         } else if (entry && *entry) {
12103                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
12104                                 g_free(newmsginfo->cc);
12105                                 newmsginfo->cc = tmp;
12106                         }
12107                 } else
12108                 if ( strcasecmp(header,
12109                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
12110                         if ( newmsginfo->newsgroups == NULL ) {
12111                                 newmsginfo->newsgroups = g_strdup(entry);
12112                         } else if (entry && *entry) {
12113                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
12114                                 g_free(newmsginfo->newsgroups);
12115                                 newmsginfo->newsgroups = tmp;
12116                         }
12117                 }
12118
12119                 g_free(header);
12120                 g_free(entry);  
12121         }
12122
12123         /* other data is unset */
12124
12125         return newmsginfo;
12126 }
12127
12128 #ifdef USE_ENCHANT
12129 /* update compose's dictionaries from folder dict settings */
12130 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
12131                                                 FolderItem *folder_item)
12132 {
12133         cm_return_if_fail(compose != NULL);
12134
12135         if (compose->gtkaspell && folder_item && folder_item->prefs) {
12136                 FolderItemPrefs *prefs = folder_item->prefs;
12137
12138                 if (prefs->enable_default_dictionary)
12139                         gtkaspell_change_dict(compose->gtkaspell,
12140                                         prefs->default_dictionary, FALSE);
12141                 if (folder_item->prefs->enable_default_alt_dictionary)
12142                         gtkaspell_change_alt_dict(compose->gtkaspell,
12143                                         prefs->default_alt_dictionary);
12144                 if (prefs->enable_default_dictionary
12145                         || prefs->enable_default_alt_dictionary)
12146                         compose_spell_menu_changed(compose);
12147         }
12148 }
12149 #endif
12150
12151 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data)
12152 {
12153         Compose *compose = (Compose *)data;
12154
12155         cm_return_if_fail(compose != NULL);
12156
12157         gtk_widget_grab_focus(compose->text);
12158 }
12159
12160 static void from_name_activate_cb(GtkWidget *widget, gpointer data)
12161 {
12162         gtk_combo_box_popup(GTK_COMBO_BOX(data));
12163 }
12164
12165
12166 /*
12167  * End of Source.
12168  */