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