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