add9867770ccb004e1e5df7ba4514033985d2ddb
[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         prefs_common.compose_width = compose->scrolledwin->allocation.width;
8035         prefs_common.compose_height = compose->window->allocation.height;
8036
8037         if (!gtk_widget_get_parent(compose->paned))
8038                 gtk_widget_destroy(compose->paned);
8039         gtk_widget_destroy(compose->popupmenu);
8040
8041         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8042         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8043         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8044
8045         gtk_widget_destroy(compose->window);
8046         toolbar_destroy(compose->toolbar);
8047         g_free(compose->toolbar);
8048         g_mutex_free(compose->mutex);
8049         g_free(compose);
8050 }
8051
8052 static void compose_attach_info_free(AttachInfo *ainfo)
8053 {
8054         g_free(ainfo->file);
8055         g_free(ainfo->content_type);
8056         g_free(ainfo->name);
8057         g_free(ainfo);
8058 }
8059
8060 static void compose_attach_update_label(Compose *compose)
8061 {
8062         GtkTreeIter iter;
8063         gint i = 1;
8064         gchar *text;
8065         GtkTreeModel *model;
8066         
8067         if(compose == NULL)
8068                 return;
8069                 
8070         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8071         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8072                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8073                 return;
8074         }
8075         
8076         while(gtk_tree_model_iter_next(model, &iter))
8077                 i++;
8078         
8079         text = g_strdup_printf("(%d)", i);
8080         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8081         g_free(text);
8082 }
8083
8084 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8085 {
8086         Compose *compose = (Compose *)data;
8087         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8088         GtkTreeSelection *selection;
8089         GList *sel, *cur;
8090         GtkTreeModel *model;
8091
8092         selection = gtk_tree_view_get_selection(tree_view);
8093         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8094
8095         if (!sel) 
8096                 return;
8097
8098         for (cur = sel; cur != NULL; cur = cur->next) {
8099                 GtkTreePath *path = cur->data;
8100                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8101                                                 (model, cur->data);
8102                 cur->data = ref;
8103                 gtk_tree_path_free(path);
8104         }
8105
8106         for (cur = sel; cur != NULL; cur = cur->next) {
8107                 GtkTreeRowReference *ref = cur->data;
8108                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8109                 GtkTreeIter iter;
8110
8111                 if (gtk_tree_model_get_iter(model, &iter, path))
8112                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8113                 
8114                 gtk_tree_path_free(path);
8115                 gtk_tree_row_reference_free(ref);
8116         }
8117
8118         g_list_free(sel);
8119         compose_attach_update_label(compose);
8120 }
8121
8122 static struct _AttachProperty
8123 {
8124         GtkWidget *window;
8125         GtkWidget *mimetype_entry;
8126         GtkWidget *encoding_optmenu;
8127         GtkWidget *path_entry;
8128         GtkWidget *filename_entry;
8129         GtkWidget *ok_btn;
8130         GtkWidget *cancel_btn;
8131 } attach_prop;
8132
8133 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8134 {       
8135         gtk_tree_path_free((GtkTreePath *)ptr);
8136 }
8137
8138 static void compose_attach_property(GtkAction *action, gpointer data)
8139 {
8140         Compose *compose = (Compose *)data;
8141         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8142         AttachInfo *ainfo;
8143         GtkComboBox *optmenu;
8144         GtkTreeSelection *selection;
8145         GList *sel;
8146         GtkTreeModel *model;
8147         GtkTreeIter iter;
8148         GtkTreePath *path;
8149         static gboolean cancelled;
8150
8151         /* only if one selected */
8152         selection = gtk_tree_view_get_selection(tree_view);
8153         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8154                 return;
8155
8156         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8157         if (!sel)
8158                 return;
8159
8160         path = (GtkTreePath *) sel->data;
8161         gtk_tree_model_get_iter(model, &iter, path);
8162         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8163         
8164         if (!ainfo) {
8165                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8166                 g_list_free(sel);
8167                 return;
8168         }               
8169         g_list_free(sel);
8170
8171         if (!attach_prop.window)
8172                 compose_attach_property_create(&cancelled);
8173         gtk_widget_grab_focus(attach_prop.ok_btn);
8174         gtk_widget_show(attach_prop.window);
8175         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8176
8177         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8178         if (ainfo->encoding == ENC_UNKNOWN)
8179                 combobox_select_by_data(optmenu, ENC_BASE64);
8180         else
8181                 combobox_select_by_data(optmenu, ainfo->encoding);
8182
8183         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8184                            ainfo->content_type ? ainfo->content_type : "");
8185         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8186                            ainfo->file ? ainfo->file : "");
8187         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8188                            ainfo->name ? ainfo->name : "");
8189
8190         for (;;) {
8191                 const gchar *entry_text;
8192                 gchar *text;
8193                 gchar *cnttype = NULL;
8194                 gchar *file = NULL;
8195                 off_t size = 0;
8196
8197                 cancelled = FALSE;
8198                 gtk_main();
8199
8200                 gtk_widget_hide(attach_prop.window);
8201                 
8202                 if (cancelled) 
8203                         break;
8204
8205                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8206                 if (*entry_text != '\0') {
8207                         gchar *p;
8208
8209                         text = g_strstrip(g_strdup(entry_text));
8210                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8211                                 cnttype = g_strdup(text);
8212                                 g_free(text);
8213                         } else {
8214                                 alertpanel_error(_("Invalid MIME type."));
8215                                 g_free(text);
8216                                 continue;
8217                         }
8218                 }
8219
8220                 ainfo->encoding = combobox_get_active_data(optmenu);
8221
8222                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8223                 if (*entry_text != '\0') {
8224                         if (is_file_exist(entry_text) &&
8225                             (size = get_file_size(entry_text)) > 0)
8226                                 file = g_strdup(entry_text);
8227                         else {
8228                                 alertpanel_error
8229                                         (_("File doesn't exist or is empty."));
8230                                 g_free(cnttype);
8231                                 continue;
8232                         }
8233                 }
8234
8235                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8236                 if (*entry_text != '\0') {
8237                         g_free(ainfo->name);
8238                         ainfo->name = g_strdup(entry_text);
8239                 }
8240
8241                 if (cnttype) {
8242                         g_free(ainfo->content_type);
8243                         ainfo->content_type = cnttype;
8244                 }
8245                 if (file) {
8246                         g_free(ainfo->file);
8247                         ainfo->file = file;
8248                 }
8249                 if (size)
8250                         ainfo->size = (goffset)size;
8251
8252                 /* update tree store */
8253                 text = to_human_readable(ainfo->size);
8254                 gtk_tree_model_get_iter(model, &iter, path);
8255                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8256                                    COL_MIMETYPE, ainfo->content_type,
8257                                    COL_SIZE, text,
8258                                    COL_NAME, ainfo->name,
8259                                    -1);
8260                 
8261                 break;
8262         }
8263
8264         gtk_tree_path_free(path);
8265 }
8266
8267 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8268 { \
8269         label = gtk_label_new(str); \
8270         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8271                          GTK_FILL, 0, 0, 0); \
8272         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8273  \
8274         entry = gtk_entry_new(); \
8275         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8276                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8277 }
8278
8279 static void compose_attach_property_create(gboolean *cancelled)
8280 {
8281         GtkWidget *window;
8282         GtkWidget *vbox;
8283         GtkWidget *table;
8284         GtkWidget *label;
8285         GtkWidget *mimetype_entry;
8286         GtkWidget *hbox;
8287         GtkWidget *optmenu;
8288         GtkListStore *optmenu_menu;
8289         GtkWidget *path_entry;
8290         GtkWidget *filename_entry;
8291         GtkWidget *hbbox;
8292         GtkWidget *ok_btn;
8293         GtkWidget *cancel_btn;
8294         GList     *mime_type_list, *strlist;
8295         GtkTreeIter iter;
8296
8297         debug_print("Creating attach_property window...\n");
8298
8299         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8300         gtk_widget_set_size_request(window, 480, -1);
8301         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8302         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8303         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8304         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8305         g_signal_connect(G_OBJECT(window), "delete_event",
8306                          G_CALLBACK(attach_property_delete_event),
8307                          cancelled);
8308         g_signal_connect(G_OBJECT(window), "key_press_event",
8309                          G_CALLBACK(attach_property_key_pressed),
8310                          cancelled);
8311
8312         vbox = gtk_vbox_new(FALSE, 8);
8313         gtk_container_add(GTK_CONTAINER(window), vbox);
8314
8315         table = gtk_table_new(4, 2, FALSE);
8316         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8317         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8318         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8319
8320         label = gtk_label_new(_("MIME type")); 
8321         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
8322                          GTK_FILL, 0, 0, 0); 
8323         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
8324         mimetype_entry = gtk_combo_box_entry_new_text(); 
8325         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
8326                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8327                          
8328         /* stuff with list */
8329         mime_type_list = procmime_get_mime_type_list();
8330         strlist = NULL;
8331         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8332                 MimeType *type = (MimeType *) mime_type_list->data;
8333                 gchar *tmp;
8334
8335                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8336
8337                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8338                         g_free(tmp);
8339                 else
8340                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8341                                         (GCompareFunc)strcmp2);
8342         }
8343
8344         for (mime_type_list = strlist; mime_type_list != NULL; 
8345                 mime_type_list = mime_type_list->next) {
8346                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8347                 g_free(mime_type_list->data);
8348         }
8349         g_list_free(strlist);
8350         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
8351         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
8352
8353         label = gtk_label_new(_("Encoding"));
8354         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8355                          GTK_FILL, 0, 0, 0);
8356         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8357
8358         hbox = gtk_hbox_new(FALSE, 0);
8359         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8360                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8361
8362         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8363         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8364
8365         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8366         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8367         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
8368         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8369         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8370
8371         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8372
8373         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
8374         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8375
8376         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8377                                       &ok_btn, GTK_STOCK_OK,
8378                                       NULL, NULL);
8379         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8380         gtk_widget_grab_default(ok_btn);
8381
8382         g_signal_connect(G_OBJECT(ok_btn), "clicked",
8383                          G_CALLBACK(attach_property_ok),
8384                          cancelled);
8385         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8386                          G_CALLBACK(attach_property_cancel),
8387                          cancelled);
8388
8389         gtk_widget_show_all(vbox);
8390
8391         attach_prop.window           = window;
8392         attach_prop.mimetype_entry   = mimetype_entry;
8393         attach_prop.encoding_optmenu = optmenu;
8394         attach_prop.path_entry       = path_entry;
8395         attach_prop.filename_entry   = filename_entry;
8396         attach_prop.ok_btn           = ok_btn;
8397         attach_prop.cancel_btn       = cancel_btn;
8398 }
8399
8400 #undef SET_LABEL_AND_ENTRY
8401
8402 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8403 {
8404         *cancelled = FALSE;
8405         gtk_main_quit();
8406 }
8407
8408 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8409 {
8410         *cancelled = TRUE;
8411         gtk_main_quit();
8412 }
8413
8414 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8415                                          gboolean *cancelled)
8416 {
8417         *cancelled = TRUE;
8418         gtk_main_quit();
8419
8420         return TRUE;
8421 }
8422
8423 static gboolean attach_property_key_pressed(GtkWidget *widget,
8424                                             GdkEventKey *event,
8425                                             gboolean *cancelled)
8426 {
8427         if (event && event->keyval == GDK_Escape) {
8428                 *cancelled = TRUE;
8429                 gtk_main_quit();
8430         }
8431         return FALSE;
8432 }
8433
8434 static void compose_exec_ext_editor(Compose *compose)
8435 {
8436 #ifdef G_OS_UNIX
8437         gchar *tmp;
8438         pid_t pid;
8439         gint pipe_fds[2];
8440
8441         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8442                               G_DIR_SEPARATOR, compose);
8443
8444         if (pipe(pipe_fds) < 0) {
8445                 perror("pipe");
8446                 g_free(tmp);
8447                 return;
8448         }
8449
8450         if ((pid = fork()) < 0) {
8451                 perror("fork");
8452                 g_free(tmp);
8453                 return;
8454         }
8455
8456         if (pid != 0) {
8457                 /* close the write side of the pipe */
8458                 close(pipe_fds[1]);
8459
8460                 compose->exteditor_file    = g_strdup(tmp);
8461                 compose->exteditor_pid     = pid;
8462
8463                 compose_set_ext_editor_sensitive(compose, FALSE);
8464
8465                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8466                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8467                                                         G_IO_IN,
8468                                                         compose_input_cb,
8469                                                         compose);
8470         } else {        /* process-monitoring process */
8471                 pid_t pid_ed;
8472
8473                 if (setpgid(0, 0))
8474                         perror("setpgid");
8475
8476                 /* close the read side of the pipe */
8477                 close(pipe_fds[0]);
8478
8479                 if (compose_write_body_to_file(compose, tmp) < 0) {
8480                         fd_write_all(pipe_fds[1], "2\n", 2);
8481                         _exit(1);
8482                 }
8483
8484                 pid_ed = compose_exec_ext_editor_real(tmp);
8485                 if (pid_ed < 0) {
8486                         fd_write_all(pipe_fds[1], "1\n", 2);
8487                         _exit(1);
8488                 }
8489
8490                 /* wait until editor is terminated */
8491                 waitpid(pid_ed, NULL, 0);
8492
8493                 fd_write_all(pipe_fds[1], "0\n", 2);
8494
8495                 close(pipe_fds[1]);
8496                 _exit(0);
8497         }
8498
8499         g_free(tmp);
8500 #endif /* G_OS_UNIX */
8501 }
8502
8503 #ifdef G_OS_UNIX
8504 static gint compose_exec_ext_editor_real(const gchar *file)
8505 {
8506         gchar buf[1024];
8507         gchar *p;
8508         gchar **cmdline;
8509         pid_t pid;
8510
8511         g_return_val_if_fail(file != NULL, -1);
8512
8513         if ((pid = fork()) < 0) {
8514                 perror("fork");
8515                 return -1;
8516         }
8517
8518         if (pid != 0) return pid;
8519
8520         /* grandchild process */
8521
8522         if (setpgid(0, getppid()))
8523                 perror("setpgid");
8524
8525         if (prefs_common_get_ext_editor_cmd() &&
8526             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8527             *(p + 1) == 's' && !strchr(p + 2, '%')) {
8528                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8529         } else {
8530                 if (prefs_common_get_ext_editor_cmd())
8531                         g_warning("External editor command line is invalid: '%s'\n",
8532                                   prefs_common_get_ext_editor_cmd());
8533                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8534         }
8535
8536         cmdline = strsplit_with_quote(buf, " ", 1024);
8537         execvp(cmdline[0], cmdline);
8538
8539         perror("execvp");
8540         g_strfreev(cmdline);
8541
8542         _exit(1);
8543 }
8544
8545 static gboolean compose_ext_editor_kill(Compose *compose)
8546 {
8547         pid_t pgid = compose->exteditor_pid * -1;
8548         gint ret;
8549
8550         ret = kill(pgid, 0);
8551
8552         if (ret == 0 || (ret == -1 && EPERM == errno)) {
8553                 AlertValue val;
8554                 gchar *msg;
8555
8556                 msg = g_strdup_printf
8557                         (_("The external editor is still working.\n"
8558                            "Force terminating the process?\n"
8559                            "process group id: %d"), -pgid);
8560                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8561                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8562                         
8563                 g_free(msg);
8564
8565                 if (val == G_ALERTALTERNATE) {
8566                         g_source_remove(compose->exteditor_tag);
8567                         g_io_channel_shutdown(compose->exteditor_ch,
8568                                               FALSE, NULL);
8569                         g_io_channel_unref(compose->exteditor_ch);
8570
8571                         if (kill(pgid, SIGTERM) < 0) perror("kill");
8572                         waitpid(compose->exteditor_pid, NULL, 0);
8573
8574                         g_warning("Terminated process group id: %d", -pgid);
8575                         g_warning("Temporary file: %s",
8576                                   compose->exteditor_file);
8577
8578                         compose_set_ext_editor_sensitive(compose, TRUE);
8579
8580                         g_free(compose->exteditor_file);
8581                         compose->exteditor_file    = NULL;
8582                         compose->exteditor_pid     = -1;
8583                         compose->exteditor_ch      = NULL;
8584                         compose->exteditor_tag     = -1;
8585                 } else
8586                         return FALSE;
8587         }
8588
8589         return TRUE;
8590 }
8591
8592 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8593                                  gpointer data)
8594 {
8595         gchar buf[3] = "3";
8596         Compose *compose = (Compose *)data;
8597         gsize bytes_read;
8598
8599         debug_print(_("Compose: input from monitoring process\n"));
8600
8601         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8602
8603         g_io_channel_shutdown(source, FALSE, NULL);
8604         g_io_channel_unref(source);
8605
8606         waitpid(compose->exteditor_pid, NULL, 0);
8607
8608         if (buf[0] == '0') {            /* success */
8609                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8610                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8611
8612                 gtk_text_buffer_set_text(buffer, "", -1);
8613                 compose_insert_file(compose, compose->exteditor_file);
8614                 compose_changed_cb(NULL, compose);
8615
8616                 if (claws_unlink(compose->exteditor_file) < 0)
8617                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8618         } else if (buf[0] == '1') {     /* failed */
8619                 g_warning("Couldn't exec external editor\n");
8620                 if (claws_unlink(compose->exteditor_file) < 0)
8621                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8622         } else if (buf[0] == '2') {
8623                 g_warning("Couldn't write to file\n");
8624         } else if (buf[0] == '3') {
8625                 g_warning("Pipe read failed\n");
8626         }
8627
8628         compose_set_ext_editor_sensitive(compose, TRUE);
8629
8630         g_free(compose->exteditor_file);
8631         compose->exteditor_file    = NULL;
8632         compose->exteditor_pid     = -1;
8633         compose->exteditor_ch      = NULL;
8634         compose->exteditor_tag     = -1;
8635
8636         return FALSE;
8637 }
8638
8639 static void compose_set_ext_editor_sensitive(Compose *compose,
8640                                              gboolean sensitive)
8641 {
8642         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8643         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8644         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8645         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8646         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8647         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8648         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8649
8650         gtk_widget_set_sensitive(compose->text,                       sensitive);
8651         if (compose->toolbar->send_btn)
8652                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
8653         if (compose->toolbar->sendl_btn)
8654                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
8655         if (compose->toolbar->draft_btn)
8656                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
8657         if (compose->toolbar->insert_btn)
8658                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
8659         if (compose->toolbar->sig_btn)
8660                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
8661         if (compose->toolbar->exteditor_btn)
8662                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8663         if (compose->toolbar->linewrap_current_btn)
8664                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8665         if (compose->toolbar->linewrap_all_btn)
8666                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8667 }
8668 #endif /* G_OS_UNIX */
8669
8670 /**
8671  * compose_undo_state_changed:
8672  *
8673  * Change the sensivity of the menuentries undo and redo
8674  **/
8675 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8676                                        gint redo_state, gpointer data)
8677 {
8678         Compose *compose = (Compose *)data;
8679
8680         switch (undo_state) {
8681         case UNDO_STATE_TRUE:
8682                 if (!undostruct->undo_state) {
8683                         undostruct->undo_state = TRUE;
8684                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8685                 }
8686                 break;
8687         case UNDO_STATE_FALSE:
8688                 if (undostruct->undo_state) {
8689                         undostruct->undo_state = FALSE;
8690                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8691                 }
8692                 break;
8693         case UNDO_STATE_UNCHANGED:
8694                 break;
8695         case UNDO_STATE_REFRESH:
8696                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8697                 break;
8698         default:
8699                 g_warning("Undo state not recognized");
8700                 break;
8701         }
8702
8703         switch (redo_state) {
8704         case UNDO_STATE_TRUE:
8705                 if (!undostruct->redo_state) {
8706                         undostruct->redo_state = TRUE;
8707                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8708                 }
8709                 break;
8710         case UNDO_STATE_FALSE:
8711                 if (undostruct->redo_state) {
8712                         undostruct->redo_state = FALSE;
8713                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8714                 }
8715                 break;
8716         case UNDO_STATE_UNCHANGED:
8717                 break;
8718         case UNDO_STATE_REFRESH:
8719                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8720                 break;
8721         default:
8722                 g_warning("Redo state not recognized");
8723                 break;
8724         }
8725 }
8726
8727 /* callback functions */
8728
8729 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8730  * includes "non-client" (windows-izm) in calculation, so this calculation
8731  * may not be accurate.
8732  */
8733 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8734                                         GtkAllocation *allocation,
8735                                         GtkSHRuler *shruler)
8736 {
8737         if (prefs_common.show_ruler) {
8738                 gint char_width = 0, char_height = 0;
8739                 gint line_width_in_chars;
8740
8741                 gtkut_get_font_size(GTK_WIDGET(widget),
8742                                     &char_width, &char_height);
8743                 line_width_in_chars =
8744                         (allocation->width - allocation->x) / char_width;
8745
8746                 /* got the maximum */
8747                 gtk_ruler_set_range(GTK_RULER(shruler),
8748                                     0.0, line_width_in_chars, 0,
8749                                     /*line_width_in_chars*/ char_width);
8750         }
8751
8752         return TRUE;
8753 }
8754
8755 static void account_activated(GtkComboBox *optmenu, gpointer data)
8756 {
8757         Compose *compose = (Compose *)data;
8758
8759         PrefsAccount *ac;
8760         gchar *folderidentifier;
8761         gint account_id = 0;
8762         GtkTreeModel *menu;
8763         GtkTreeIter iter;
8764
8765         /* Get ID of active account in the combo box */
8766         menu = gtk_combo_box_get_model(optmenu);
8767         gtk_combo_box_get_active_iter(optmenu, &iter);
8768         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8769
8770         ac = account_find_from_id(account_id);
8771         g_return_if_fail(ac != NULL);
8772
8773         if (ac != compose->account)
8774                 compose_select_account(compose, ac, FALSE);
8775
8776         /* Set message save folder */
8777         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8778                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8779         }
8780         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8781                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8782                            
8783         gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8784         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8785                 folderidentifier = folder_item_get_identifier(account_get_special_folder
8786                                   (compose->account, F_OUTBOX));
8787                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8788                 g_free(folderidentifier);
8789         }
8790 }
8791
8792 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8793                             GtkTreeViewColumn *column, Compose *compose)
8794 {
8795         compose_attach_property(NULL, compose);
8796 }
8797
8798 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8799                                       gpointer data)
8800 {
8801         Compose *compose = (Compose *)data;
8802         GtkTreeSelection *attach_selection;
8803         gint attach_nr_selected;
8804         
8805         if (!event) return FALSE;
8806
8807         if (event->button == 3) {
8808                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8809                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8810                         
8811                 if (attach_nr_selected > 0)
8812                 {
8813                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8814                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8815                 } else {
8816                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8817                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8818                 }
8819                         
8820                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8821                                NULL, NULL, event->button, event->time);
8822                 return TRUE;                           
8823         }
8824
8825         return FALSE;
8826 }
8827
8828 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8829                                    gpointer data)
8830 {
8831         Compose *compose = (Compose *)data;
8832
8833         if (!event) return FALSE;
8834
8835         switch (event->keyval) {
8836         case GDK_Delete:
8837                 compose_attach_remove_selected(NULL, compose);
8838                 break;
8839         }
8840         return FALSE;
8841 }
8842
8843 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8844 {
8845         toolbar_comp_set_sensitive(compose, allow);
8846         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8847         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8848 #if USE_ASPELL
8849         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8850 #endif  
8851         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8852         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8853         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8854         
8855         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8856
8857 }
8858
8859 static void compose_send_cb(GtkAction *action, gpointer data)
8860 {
8861         Compose *compose = (Compose *)data;
8862         
8863         if (prefs_common.work_offline && 
8864             !inc_offline_should_override(TRUE,
8865                 _("Claws Mail needs network access in order "
8866                   "to send this email.")))
8867                 return;
8868         
8869         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8870                 g_source_remove(compose->draft_timeout_tag);
8871                 compose->draft_timeout_tag = -1;
8872         }
8873
8874         compose_send(compose);
8875 }
8876
8877 static void compose_send_later_cb(GtkAction *action, gpointer data)
8878 {
8879         Compose *compose = (Compose *)data;
8880         gint val;
8881
8882         inc_lock();
8883         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8884         inc_unlock();
8885
8886         if (!val) {
8887                 compose_close(compose);
8888         } else if (val == -1) {
8889                 alertpanel_error(_("Could not queue message."));
8890         } else if (val == -2) {
8891                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8892         } else if (val == -3) {
8893                 if (privacy_peek_error())
8894                 alertpanel_error(_("Could not queue message for sending:\n\n"
8895                                    "Signature failed: %s"), privacy_get_error());
8896         } else if (val == -4) {
8897                 alertpanel_error(_("Could not queue message for sending:\n\n"
8898                                    "Charset conversion failed."));
8899         } else if (val == -5) {
8900                 alertpanel_error(_("Could not queue message for sending:\n\n"
8901                                    "Couldn't get recipient encryption key."));
8902         } else if (val == -6) {
8903                 /* silent error */
8904         }
8905         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8906 }
8907
8908 #define DRAFTED_AT_EXIT "drafted_at_exit"
8909 static void compose_register_draft(MsgInfo *info)
8910 {
8911         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8912                                       DRAFTED_AT_EXIT, NULL);
8913         FILE *fp = fopen(filepath, "ab");
8914         
8915         if (fp) {
8916                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
8917                                 info->msgnum);
8918                 fclose(fp);
8919         }
8920                 
8921         g_free(filepath);       
8922 }
8923
8924 gboolean compose_draft (gpointer data, guint action) 
8925 {
8926         Compose *compose = (Compose *)data;
8927         FolderItem *draft;
8928         gchar *tmp;
8929         gint msgnum;
8930         MsgFlags flag = {0, 0};
8931         static gboolean lock = FALSE;
8932         MsgInfo *newmsginfo;
8933         FILE *fp;
8934         gboolean target_locked = FALSE;
8935         gboolean err = FALSE;
8936
8937         if (lock) return FALSE;
8938
8939         if (compose->sending)
8940                 return TRUE;
8941
8942         draft = account_get_special_folder(compose->account, F_DRAFT);
8943         g_return_val_if_fail(draft != NULL, FALSE);
8944         
8945         if (!g_mutex_trylock(compose->mutex)) {
8946                 /* we don't want to lock the mutex once it's available,
8947                  * because as the only other part of compose.c locking
8948                  * it is compose_close - which means once unlocked,
8949                  * the compose struct will be freed */
8950                 debug_print("couldn't lock mutex, probably sending\n");
8951                 return FALSE;
8952         }
8953         
8954         lock = TRUE;
8955
8956         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8957                               G_DIR_SEPARATOR, compose);
8958         if ((fp = g_fopen(tmp, "wb")) == NULL) {
8959                 FILE_OP_ERROR(tmp, "fopen");
8960                 goto warn_err;
8961         }
8962
8963         /* chmod for security */
8964         if (change_file_mode_rw(fp, tmp) < 0) {
8965                 FILE_OP_ERROR(tmp, "chmod");
8966                 g_warning("can't change file mode\n");
8967         }
8968
8969         /* Save draft infos */
8970         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8971         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8972
8973         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8974                 gchar *savefolderid;
8975
8976                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8977                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8978                 g_free(savefolderid);
8979         }
8980         if (compose->return_receipt) {
8981                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8982         }
8983         if (compose->privacy_system) {
8984                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8985                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8986                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8987         }
8988
8989         /* Message-ID of message replying to */
8990         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8991                 gchar *folderid;
8992                 
8993                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8994                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8995                 g_free(folderid);
8996         }
8997         /* Message-ID of message forwarding to */
8998         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8999                 gchar *folderid;
9000                 
9001                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
9002                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
9003                 g_free(folderid);
9004         }
9005
9006         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
9007         err |= (fprintf(fp, "X-Claws-Auto-Indent:%d\n", compose->autoindent) < 0);
9008
9009         /* end of headers */
9010         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
9011
9012         if (err) {
9013                 fclose(fp);
9014                 goto warn_err;
9015         }
9016
9017         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
9018                 fclose(fp);
9019                 goto warn_err;
9020         }
9021         if (fclose(fp) == EOF) {
9022                 goto warn_err;
9023         }
9024         
9025         if (compose->targetinfo) {
9026                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9027                 flag.perm_flags = target_locked?MSG_LOCKED:0;
9028         }
9029         flag.tmp_flags = MSG_DRAFT;
9030
9031         folder_item_scan(draft);
9032         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9033                 MsgInfo *tmpinfo = NULL;
9034                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9035                 if (compose->msgid) {
9036                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9037                 }
9038                 if (tmpinfo) {
9039                         msgnum = tmpinfo->msgnum;
9040                         procmsg_msginfo_free(tmpinfo);
9041                         debug_print("got draft msgnum %d from scanning\n", msgnum);
9042                 } else {
9043                         debug_print("didn't get draft msgnum after scanning\n");
9044                 }
9045         } else {
9046                 debug_print("got draft msgnum %d from adding\n", msgnum);
9047         }
9048         if (msgnum < 0) {
9049 warn_err:
9050                 claws_unlink(tmp);
9051                 g_free(tmp);
9052                 if (action != COMPOSE_AUTO_SAVE) {
9053                         if (action != COMPOSE_DRAFT_FOR_EXIT)
9054                                 alertpanel_error(_("Could not save draft."));
9055                         else {
9056                                 AlertValue val;
9057                                 gtkut_window_popup(compose->window);
9058                                 val = alertpanel_full(_("Could not save draft"),
9059                                         _("Could not save draft.\n"
9060                                         "Do you want to cancel exit or discard this email?"),
9061                                           _("_Cancel exit"), _("_Discard email"), NULL,
9062                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9063                                 if (val == G_ALERTALTERNATE) {
9064                                         lock = FALSE;
9065                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9066                                         compose_close(compose);
9067                                         return TRUE;
9068                                 } else {
9069                                         lock = FALSE;
9070                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9071                                         return FALSE;
9072                                 }
9073                         }
9074                 }
9075                 goto unlock;
9076         }
9077         g_free(tmp);
9078
9079         if (compose->mode == COMPOSE_REEDIT) {
9080                 compose_remove_reedit_target(compose, TRUE);
9081         }
9082
9083         newmsginfo = folder_item_get_msginfo(draft, msgnum);
9084
9085         if (newmsginfo) {
9086                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9087                 if (target_locked)
9088                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9089                 else
9090                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9091                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9092                         procmsg_msginfo_set_flags(newmsginfo, 0,
9093                                                   MSG_HAS_ATTACHMENT);
9094
9095                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9096                         compose_register_draft(newmsginfo);
9097                 }
9098                 procmsg_msginfo_free(newmsginfo);
9099         }
9100         
9101         folder_item_scan(draft);
9102         
9103         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9104                 lock = FALSE;
9105                 g_mutex_unlock(compose->mutex); /* must be done before closing */
9106                 compose_close(compose);
9107                 return TRUE;
9108         } else {
9109                 struct stat s;
9110                 gchar *path;
9111
9112                 path = folder_item_fetch_msg(draft, msgnum);
9113                 if (path == NULL) {
9114                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9115                         goto unlock;
9116                 }
9117                 if (g_stat(path, &s) < 0) {
9118                         FILE_OP_ERROR(path, "stat");
9119                         g_free(path);
9120                         goto unlock;
9121                 }
9122                 g_free(path);
9123
9124                 procmsg_msginfo_free(compose->targetinfo);
9125                 compose->targetinfo = procmsg_msginfo_new();
9126                 compose->targetinfo->msgnum = msgnum;
9127                 compose->targetinfo->size = (goffset)s.st_size;
9128                 compose->targetinfo->mtime = s.st_mtime;
9129                 compose->targetinfo->folder = draft;
9130                 if (target_locked)
9131                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9132                 compose->mode = COMPOSE_REEDIT;
9133                 
9134                 if (action == COMPOSE_AUTO_SAVE) {
9135                         compose->autosaved_draft = compose->targetinfo;
9136                 }
9137                 compose->modified = FALSE;
9138                 compose_set_title(compose);
9139         }
9140 unlock:
9141         lock = FALSE;
9142         g_mutex_unlock(compose->mutex);
9143         return TRUE;
9144 }
9145
9146 void compose_clear_exit_drafts(void)
9147 {
9148         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9149                                       DRAFTED_AT_EXIT, NULL);
9150         if (is_file_exist(filepath))
9151                 claws_unlink(filepath);
9152         
9153         g_free(filepath);
9154 }
9155
9156 void compose_reopen_exit_drafts(void)
9157 {
9158         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9159                                       DRAFTED_AT_EXIT, NULL);
9160         FILE *fp = fopen(filepath, "rb");
9161         gchar buf[1024];
9162         
9163         if (fp) {
9164                 while (fgets(buf, sizeof(buf), fp)) {
9165                         gchar **parts = g_strsplit(buf, "\t", 2);
9166                         const gchar *folder = parts[0];
9167                         int msgnum = parts[1] ? atoi(parts[1]):-1;
9168                         
9169                         if (folder && *folder && msgnum > -1) {
9170                                 FolderItem *item = folder_find_item_from_identifier(folder);
9171                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9172                                 if (info)
9173                                         compose_reedit(info, FALSE);
9174                         }
9175                         g_strfreev(parts);
9176                 }       
9177                 fclose(fp);
9178         }       
9179         g_free(filepath);
9180         compose_clear_exit_drafts();
9181 }
9182
9183 static void compose_save_cb(GtkAction *action, gpointer data)
9184 {
9185         compose_draft(data, COMPOSE_KEEP_EDITING);
9186 }
9187
9188 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9189 {
9190         if (compose && file_list) {
9191                 GList *tmp;
9192
9193                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9194                         gchar *file = (gchar *) tmp->data;
9195                         gchar *utf8_filename = conv_filename_to_utf8(file);
9196                         compose_attach_append(compose, file, utf8_filename, NULL);
9197                         compose_changed_cb(NULL, compose);
9198                         if (free_data) {
9199                         g_free(file);
9200                                 tmp->data = NULL;
9201                         }
9202                         g_free(utf8_filename);
9203                 }
9204         }
9205 }
9206
9207 static void compose_attach_cb(GtkAction *action, gpointer data)
9208 {
9209         Compose *compose = (Compose *)data;
9210         GList *file_list;
9211
9212         if (compose->redirect_filename != NULL)
9213                 return;
9214
9215         file_list = filesel_select_multiple_files_open(_("Select file"));
9216
9217         if (file_list) {
9218                 compose_attach_from_list(compose, file_list, TRUE);
9219                 g_list_free(file_list);
9220         }
9221 }
9222
9223 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9224 {
9225         Compose *compose = (Compose *)data;
9226         GList *file_list;
9227
9228         file_list = filesel_select_multiple_files_open(_("Select file"));
9229
9230         if (file_list) {
9231                 GList *tmp;
9232
9233                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9234                         gchar *file = (gchar *) tmp->data;
9235                         gchar *filedup = g_strdup(file);
9236                         gchar *shortfile = g_path_get_basename(filedup);
9237                         ComposeInsertResult res;
9238
9239                         res = compose_insert_file(compose, file);
9240                         if (res == COMPOSE_INSERT_READ_ERROR) {
9241                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
9242                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9243                                 alertpanel_error(_("File '%s' contained invalid characters\n"
9244                                                    "for the current encoding, insertion may be incorrect."), shortfile);
9245                         }
9246                         g_free(shortfile);
9247                         g_free(filedup);
9248                         g_free(file);
9249                 }
9250                 g_list_free(file_list);
9251         }
9252 }
9253
9254 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9255 {
9256         Compose *compose = (Compose *)data;
9257
9258         compose_insert_sig(compose, FALSE);
9259 }
9260
9261 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9262                               gpointer data)
9263 {
9264         gint x, y;
9265         Compose *compose = (Compose *)data;
9266
9267         gtkut_widget_get_uposition(widget, &x, &y);
9268         prefs_common.compose_x = x;
9269         prefs_common.compose_y = y;
9270
9271         if (compose->sending || compose->updating)
9272                 return TRUE;
9273         compose_close_cb(NULL, compose);
9274         return TRUE;
9275 }
9276
9277 void compose_close_toolbar(Compose *compose)
9278 {
9279         compose_close_cb(NULL, compose);
9280 }
9281
9282 static void compose_close_cb(GtkAction *action, gpointer data)
9283 {
9284         Compose *compose = (Compose *)data;
9285         AlertValue val;
9286
9287 #ifdef G_OS_UNIX
9288         if (compose->exteditor_tag != -1) {
9289                 if (!compose_ext_editor_kill(compose))
9290                         return;
9291         }
9292 #endif
9293
9294         if (compose->modified) {
9295                 gboolean reedit = (compose->rmode == COMPOSE_REEDIT);
9296                 if (!g_mutex_trylock(compose->mutex)) {
9297                         /* we don't want to lock the mutex once it's available,
9298                          * because as the only other part of compose.c locking
9299                          * it is compose_close - which means once unlocked,
9300                          * the compose struct will be freed */
9301                         debug_print("couldn't lock mutex, probably sending\n");
9302                         return;
9303                 }
9304                 if (!reedit) {
9305                         val = alertpanel(_("Discard message"),
9306                                  _("This message has been modified. Discard it?"),
9307                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9308                 } else {
9309                         val = alertpanel(_("Save changes"),
9310                                  _("This message has been modified. Save the latest changes?"),
9311                                  _("_Don't save"), _("+_Save to Drafts"), GTK_STOCK_CANCEL);
9312                 }
9313                 g_mutex_unlock(compose->mutex);
9314                 switch (val) {
9315                 case G_ALERTDEFAULT:
9316                         if (prefs_common.autosave && !reedit)
9317                                 compose_remove_draft(compose);                  
9318                         break;
9319                 case G_ALERTALTERNATE:
9320                         compose_draft(data, COMPOSE_QUIT_EDITING);
9321                         return;
9322                 default:
9323                         return;
9324                 }
9325         }
9326
9327         compose_close(compose);
9328 }
9329
9330 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9331 {
9332         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9333         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9334         Compose *compose = (Compose *) data;
9335
9336         if (active)
9337                 compose->out_encoding = (CharSet)value;
9338 }
9339
9340 static void compose_address_cb(GtkAction *action, gpointer data)
9341 {
9342         Compose *compose = (Compose *)data;
9343
9344         addressbook_open(compose);
9345 }
9346
9347 static void about_show_cb(GtkAction *action, gpointer data)
9348 {
9349         about_show();
9350 }
9351
9352 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9353 {
9354         Compose *compose = (Compose *)data;
9355         Template *tmpl;
9356         gchar *msg;
9357         AlertValue val;
9358
9359         tmpl = g_object_get_data(G_OBJECT(widget), "template");
9360         g_return_if_fail(tmpl != NULL);
9361
9362         msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9363                               tmpl->name);
9364         val = alertpanel(_("Apply template"), msg,
9365                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9366         g_free(msg);
9367
9368         if (val == G_ALERTDEFAULT)
9369                 compose_template_apply(compose, tmpl, TRUE);
9370         else if (val == G_ALERTALTERNATE)
9371                 compose_template_apply(compose, tmpl, FALSE);
9372 }
9373
9374 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9375 {
9376         Compose *compose = (Compose *)data;
9377
9378         compose_exec_ext_editor(compose);
9379 }
9380
9381 static void compose_undo_cb(GtkAction *action, gpointer data)
9382 {
9383         Compose *compose = (Compose *)data;
9384         gboolean prev_autowrap = compose->autowrap;
9385
9386         compose->autowrap = FALSE;
9387         undo_undo(compose->undostruct);
9388         compose->autowrap = prev_autowrap;
9389 }
9390
9391 static void compose_redo_cb(GtkAction *action, gpointer data)
9392 {
9393         Compose *compose = (Compose *)data;
9394         gboolean prev_autowrap = compose->autowrap;
9395         
9396         compose->autowrap = FALSE;
9397         undo_redo(compose->undostruct);
9398         compose->autowrap = prev_autowrap;
9399 }
9400
9401 static void entry_cut_clipboard(GtkWidget *entry)
9402 {
9403         if (GTK_IS_EDITABLE(entry))
9404                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9405         else if (GTK_IS_TEXT_VIEW(entry))
9406                 gtk_text_buffer_cut_clipboard(
9407                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9408                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9409                         TRUE);
9410 }
9411
9412 static void entry_copy_clipboard(GtkWidget *entry)
9413 {
9414         if (GTK_IS_EDITABLE(entry))
9415                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9416         else if (GTK_IS_TEXT_VIEW(entry))
9417                 gtk_text_buffer_copy_clipboard(
9418                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9419                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9420 }
9421
9422 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
9423                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9424 {
9425         if (GTK_IS_TEXT_VIEW(entry)) {
9426                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9427                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9428                 GtkTextIter start_iter, end_iter;
9429                 gint start, end;
9430                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9431
9432                 if (contents == NULL)
9433                         return;
9434         
9435                 /* we shouldn't delete the selection when middle-click-pasting, or we
9436                  * can't mid-click-paste our own selection */
9437                 if (clip != GDK_SELECTION_PRIMARY) {
9438                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9439                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9440                 }
9441                 
9442                 if (insert_place == NULL) {
9443                         /* if insert_place isn't specified, insert at the cursor.
9444                          * used for Ctrl-V pasting */
9445                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9446                         start = gtk_text_iter_get_offset(&start_iter);
9447                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9448                 } else {
9449                         /* if insert_place is specified, paste here.
9450                          * used for mid-click-pasting */
9451                         start = gtk_text_iter_get_offset(insert_place);
9452                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9453                 }
9454                 
9455                 if (!wrap) {
9456                         /* paste unwrapped: mark the paste so it's not wrapped later */
9457                         end = start + strlen(contents);
9458                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9459                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9460                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9461                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9462                         /* rewrap paragraph now (after a mid-click-paste) */
9463                         mark_start = gtk_text_buffer_get_insert(buffer);
9464                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9465                         gtk_text_iter_backward_char(&start_iter);
9466                         compose_beautify_paragraph(compose, &start_iter, TRUE);
9467                 }
9468         } else if (GTK_IS_EDITABLE(entry))
9469                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9470
9471         compose->modified = TRUE;
9472 }
9473
9474 static void entry_allsel(GtkWidget *entry)
9475 {
9476         if (GTK_IS_EDITABLE(entry))
9477                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9478         else if (GTK_IS_TEXT_VIEW(entry)) {
9479                 GtkTextIter startiter, enditer;
9480                 GtkTextBuffer *textbuf;
9481
9482                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9483                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9484                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9485
9486                 gtk_text_buffer_move_mark_by_name(textbuf, 
9487                         "selection_bound", &startiter);
9488                 gtk_text_buffer_move_mark_by_name(textbuf, 
9489                         "insert", &enditer);
9490         }
9491 }
9492
9493 static void compose_cut_cb(GtkAction *action, gpointer data)
9494 {
9495         Compose *compose = (Compose *)data;
9496         if (compose->focused_editable 
9497 #ifndef GENERIC_UMPC
9498             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9499 #endif
9500             )
9501                 entry_cut_clipboard(compose->focused_editable);
9502 }
9503
9504 static void compose_copy_cb(GtkAction *action, gpointer data)
9505 {
9506         Compose *compose = (Compose *)data;
9507         if (compose->focused_editable 
9508 #ifndef GENERIC_UMPC
9509             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9510 #endif
9511             )
9512                 entry_copy_clipboard(compose->focused_editable);
9513 }
9514
9515 static void compose_paste_cb(GtkAction *action, gpointer data)
9516 {
9517         Compose *compose = (Compose *)data;
9518         gint prev_autowrap;
9519         GtkTextBuffer *buffer;
9520         BLOCK_WRAP();
9521         if (compose->focused_editable &&
9522             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9523                 entry_paste_clipboard(compose, compose->focused_editable, 
9524                                 prefs_common.linewrap_pastes,
9525                                 GDK_SELECTION_CLIPBOARD, NULL);
9526         UNBLOCK_WRAP();
9527 }
9528
9529 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9530 {
9531         Compose *compose = (Compose *)data;
9532         gint wrap_quote = prefs_common.linewrap_quote;
9533         if (compose->focused_editable 
9534 #ifndef GENERIC_UMPC
9535             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9536 #endif
9537             ) {
9538                 /* let text_insert() (called directly or at a later time
9539                  * after the gtk_editable_paste_clipboard) know that 
9540                  * text is to be inserted as a quotation. implemented
9541                  * by using a simple refcount... */
9542                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9543                                                 G_OBJECT(compose->focused_editable),
9544                                                 "paste_as_quotation"));
9545                 g_object_set_data(G_OBJECT(compose->focused_editable),
9546                                     "paste_as_quotation",
9547                                     GINT_TO_POINTER(paste_as_quotation + 1));
9548                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9549                 entry_paste_clipboard(compose, compose->focused_editable, 
9550                                 prefs_common.linewrap_pastes,
9551                                 GDK_SELECTION_CLIPBOARD, NULL);
9552                 prefs_common.linewrap_quote = wrap_quote;
9553         }
9554 }
9555
9556 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9557 {
9558         Compose *compose = (Compose *)data;
9559         gint prev_autowrap;
9560         GtkTextBuffer *buffer;
9561         BLOCK_WRAP();
9562         if (compose->focused_editable 
9563 #ifndef GENERIC_UMPC
9564             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9565 #endif
9566             )
9567                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9568                         GDK_SELECTION_CLIPBOARD, NULL);
9569         UNBLOCK_WRAP();
9570 }
9571
9572 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9573 {
9574         Compose *compose = (Compose *)data;
9575         gint prev_autowrap;
9576         GtkTextBuffer *buffer;
9577         BLOCK_WRAP();
9578         if (compose->focused_editable 
9579 #ifndef GENERIC_UMPC
9580             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9581 #endif
9582             )
9583                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9584                         GDK_SELECTION_CLIPBOARD, NULL);
9585         UNBLOCK_WRAP();
9586 }
9587
9588 static void compose_allsel_cb(GtkAction *action, gpointer data)
9589 {
9590         Compose *compose = (Compose *)data;
9591         if (compose->focused_editable 
9592 #ifndef GENERIC_UMPC
9593             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9594 #endif
9595             )
9596                 entry_allsel(compose->focused_editable);
9597 }
9598
9599 static void textview_move_beginning_of_line (GtkTextView *text)
9600 {
9601         GtkTextBuffer *buffer;
9602         GtkTextMark *mark;
9603         GtkTextIter ins;
9604
9605         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9606
9607         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9608         mark = gtk_text_buffer_get_insert(buffer);
9609         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9610         gtk_text_iter_set_line_offset(&ins, 0);
9611         gtk_text_buffer_place_cursor(buffer, &ins);
9612 }
9613
9614 static void textview_move_forward_character (GtkTextView *text)
9615 {
9616         GtkTextBuffer *buffer;
9617         GtkTextMark *mark;
9618         GtkTextIter ins;
9619
9620         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9621
9622         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9623         mark = gtk_text_buffer_get_insert(buffer);
9624         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9625         if (gtk_text_iter_forward_cursor_position(&ins))
9626                 gtk_text_buffer_place_cursor(buffer, &ins);
9627 }
9628
9629 static void textview_move_backward_character (GtkTextView *text)
9630 {
9631         GtkTextBuffer *buffer;
9632         GtkTextMark *mark;
9633         GtkTextIter ins;
9634
9635         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9636
9637         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9638         mark = gtk_text_buffer_get_insert(buffer);
9639         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9640         if (gtk_text_iter_backward_cursor_position(&ins))
9641                 gtk_text_buffer_place_cursor(buffer, &ins);
9642 }
9643
9644 static void textview_move_forward_word (GtkTextView *text)
9645 {
9646         GtkTextBuffer *buffer;
9647         GtkTextMark *mark;
9648         GtkTextIter ins;
9649         gint count;
9650
9651         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9652
9653         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9654         mark = gtk_text_buffer_get_insert(buffer);
9655         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9656         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9657         if (gtk_text_iter_forward_word_ends(&ins, count)) {
9658                 gtk_text_iter_backward_word_start(&ins);
9659                 gtk_text_buffer_place_cursor(buffer, &ins);
9660         }
9661 }
9662
9663 static void textview_move_backward_word (GtkTextView *text)
9664 {
9665         GtkTextBuffer *buffer;
9666         GtkTextMark *mark;
9667         GtkTextIter ins;
9668         gint count;
9669
9670         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9671
9672         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9673         mark = gtk_text_buffer_get_insert(buffer);
9674         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9675         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9676         if (gtk_text_iter_backward_word_starts(&ins, 1))
9677                 gtk_text_buffer_place_cursor(buffer, &ins);
9678 }
9679
9680 static void textview_move_end_of_line (GtkTextView *text)
9681 {
9682         GtkTextBuffer *buffer;
9683         GtkTextMark *mark;
9684         GtkTextIter ins;
9685
9686         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9687
9688         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9689         mark = gtk_text_buffer_get_insert(buffer);
9690         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9691         if (gtk_text_iter_forward_to_line_end(&ins))
9692                 gtk_text_buffer_place_cursor(buffer, &ins);
9693 }
9694
9695 static void textview_move_next_line (GtkTextView *text)
9696 {
9697         GtkTextBuffer *buffer;
9698         GtkTextMark *mark;
9699         GtkTextIter ins;
9700         gint offset;
9701
9702         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9703
9704         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9705         mark = gtk_text_buffer_get_insert(buffer);
9706         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9707         offset = gtk_text_iter_get_line_offset(&ins);
9708         if (gtk_text_iter_forward_line(&ins)) {
9709                 gtk_text_iter_set_line_offset(&ins, offset);
9710                 gtk_text_buffer_place_cursor(buffer, &ins);
9711         }
9712 }
9713
9714 static void textview_move_previous_line (GtkTextView *text)
9715 {
9716         GtkTextBuffer *buffer;
9717         GtkTextMark *mark;
9718         GtkTextIter ins;
9719         gint offset;
9720
9721         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9722
9723         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9724         mark = gtk_text_buffer_get_insert(buffer);
9725         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9726         offset = gtk_text_iter_get_line_offset(&ins);
9727         if (gtk_text_iter_backward_line(&ins)) {
9728                 gtk_text_iter_set_line_offset(&ins, offset);
9729                 gtk_text_buffer_place_cursor(buffer, &ins);
9730         }
9731 }
9732
9733 static void textview_delete_forward_character (GtkTextView *text)
9734 {
9735         GtkTextBuffer *buffer;
9736         GtkTextMark *mark;
9737         GtkTextIter ins, end_iter;
9738
9739         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9740
9741         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9742         mark = gtk_text_buffer_get_insert(buffer);
9743         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9744         end_iter = ins;
9745         if (gtk_text_iter_forward_char(&end_iter)) {
9746                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9747         }
9748 }
9749
9750 static void textview_delete_backward_character (GtkTextView *text)
9751 {
9752         GtkTextBuffer *buffer;
9753         GtkTextMark *mark;
9754         GtkTextIter ins, end_iter;
9755
9756         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9757
9758         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9759         mark = gtk_text_buffer_get_insert(buffer);
9760         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9761         end_iter = ins;
9762         if (gtk_text_iter_backward_char(&end_iter)) {
9763                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9764         }
9765 }
9766
9767 static void textview_delete_forward_word (GtkTextView *text)
9768 {
9769         GtkTextBuffer *buffer;
9770         GtkTextMark *mark;
9771         GtkTextIter ins, end_iter;
9772
9773         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9774
9775         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9776         mark = gtk_text_buffer_get_insert(buffer);
9777         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9778         end_iter = ins;
9779         if (gtk_text_iter_forward_word_end(&end_iter)) {
9780                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9781         }
9782 }
9783
9784 static void textview_delete_backward_word (GtkTextView *text)
9785 {
9786         GtkTextBuffer *buffer;
9787         GtkTextMark *mark;
9788         GtkTextIter ins, end_iter;
9789
9790         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9791
9792         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9793         mark = gtk_text_buffer_get_insert(buffer);
9794         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9795         end_iter = ins;
9796         if (gtk_text_iter_backward_word_start(&end_iter)) {
9797                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9798         }
9799 }
9800
9801 static void textview_delete_line (GtkTextView *text)
9802 {
9803         GtkTextBuffer *buffer;
9804         GtkTextMark *mark;
9805         GtkTextIter ins, start_iter, end_iter;
9806
9807         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9808
9809         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9810         mark = gtk_text_buffer_get_insert(buffer);
9811         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9812
9813         start_iter = ins;
9814         gtk_text_iter_set_line_offset(&start_iter, 0);
9815
9816         end_iter = ins;
9817         if (gtk_text_iter_ends_line(&end_iter)){
9818                 if (!gtk_text_iter_forward_char(&end_iter))
9819                         gtk_text_iter_backward_char(&start_iter);
9820         }
9821         else 
9822                 gtk_text_iter_forward_to_line_end(&end_iter);
9823         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9824 }
9825
9826 static void textview_delete_to_line_end (GtkTextView *text)
9827 {
9828         GtkTextBuffer *buffer;
9829         GtkTextMark *mark;
9830         GtkTextIter ins, end_iter;
9831
9832         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9833
9834         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9835         mark = gtk_text_buffer_get_insert(buffer);
9836         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9837         end_iter = ins;
9838         if (gtk_text_iter_ends_line(&end_iter))
9839                 gtk_text_iter_forward_char(&end_iter);
9840         else
9841                 gtk_text_iter_forward_to_line_end(&end_iter);
9842         gtk_text_buffer_delete(buffer, &ins, &end_iter);
9843 }
9844
9845 #define DO_ACTION(name, act) {                                          \
9846         if(!strcmp(name, a_name)) {                                     \
9847                 return act;                                             \
9848         }                                                               \
9849 }
9850 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9851 {
9852         const gchar *a_name = gtk_action_get_name(action);
9853         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9854         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9855         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9856         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9857         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9858         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9859         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9860         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9861         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9862         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9863         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9864         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9865         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9866         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9867         return -1;
9868 }
9869
9870 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9871 {
9872         Compose *compose = (Compose *)data;
9873         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9874         ComposeCallAdvancedAction action = -1;
9875         
9876         action = compose_call_advanced_action_from_path(gaction);
9877
9878         static struct {
9879                 void (*do_action) (GtkTextView *text);
9880         } action_table[] = {
9881                 {textview_move_beginning_of_line},
9882                 {textview_move_forward_character},
9883                 {textview_move_backward_character},
9884                 {textview_move_forward_word},
9885                 {textview_move_backward_word},
9886                 {textview_move_end_of_line},
9887                 {textview_move_next_line},
9888                 {textview_move_previous_line},
9889                 {textview_delete_forward_character},
9890                 {textview_delete_backward_character},
9891                 {textview_delete_forward_word},
9892                 {textview_delete_backward_word},
9893                 {textview_delete_line},
9894                 {textview_delete_to_line_end}
9895         };
9896
9897         if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9898
9899         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9900             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9901                 if (action_table[action].do_action)
9902                         action_table[action].do_action(text);
9903                 else
9904                         g_warning("Not implemented yet.");
9905         }
9906 }
9907
9908 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9909 {
9910         gchar *str = NULL;
9911         
9912         if (GTK_IS_EDITABLE(widget)) {
9913                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9914                 gtk_editable_set_position(GTK_EDITABLE(widget), 
9915                         strlen(str));
9916                 g_free(str);
9917                 if (widget->parent && widget->parent->parent
9918                  && widget->parent->parent->parent) {
9919                         if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9920                                 gint y = widget->allocation.y;
9921                                 gint height = widget->allocation.height;
9922                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9923                                         (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9924
9925                                 if (y < (int)shown->value) {
9926                                         gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9927                                 }
9928                                 if (y + height > (int)shown->value + (int)shown->page_size) {
9929                                         if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9930                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9931                                                         y + height - (int)shown->page_size - 1);
9932                                         } else {
9933                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9934                                                         (int)shown->upper - (int)shown->page_size - 1);
9935                                         }
9936                                 }
9937                         }
9938                 }
9939         }
9940
9941         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9942                 compose->focused_editable = widget;
9943         
9944 #ifdef GENERIC_UMPC
9945         if (GTK_IS_TEXT_VIEW(widget) 
9946             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9947                 g_object_ref(compose->notebook);
9948                 g_object_ref(compose->edit_vbox);
9949                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9950                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9951                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9952                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9953                 g_object_unref(compose->notebook);
9954                 g_object_unref(compose->edit_vbox);
9955                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9956                                         G_CALLBACK(compose_grab_focus_cb),
9957                                         compose);
9958                 gtk_widget_grab_focus(widget);
9959                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9960                                         G_CALLBACK(compose_grab_focus_cb),
9961                                         compose);
9962         } else if (!GTK_IS_TEXT_VIEW(widget) 
9963                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9964                 g_object_ref(compose->notebook);
9965                 g_object_ref(compose->edit_vbox);
9966                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9967                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9968                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9969                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9970                 g_object_unref(compose->notebook);
9971                 g_object_unref(compose->edit_vbox);
9972                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9973                                         G_CALLBACK(compose_grab_focus_cb),
9974                                         compose);
9975                 gtk_widget_grab_focus(widget);
9976                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9977                                         G_CALLBACK(compose_grab_focus_cb),
9978                                         compose);
9979         }
9980 #endif
9981 }
9982
9983 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9984 {
9985         compose->modified = TRUE;
9986 #ifndef GENERIC_UMPC
9987         compose_set_title(compose);
9988 #endif
9989 }
9990
9991 static void compose_wrap_cb(GtkAction *action, gpointer data)
9992 {
9993         Compose *compose = (Compose *)data;
9994         compose_beautify_paragraph(compose, NULL, TRUE);
9995 }
9996
9997 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
9998 {
9999         Compose *compose = (Compose *)data;
10000         compose_wrap_all_full(compose, TRUE);
10001 }
10002
10003 static void compose_find_cb(GtkAction *action, gpointer data)
10004 {
10005         Compose *compose = (Compose *)data;
10006
10007         message_search_compose(compose);
10008 }
10009
10010 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
10011                                          gpointer        data)
10012 {
10013         Compose *compose = (Compose *)data;
10014         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10015         if (compose->autowrap)
10016                 compose_wrap_all_full(compose, TRUE);
10017         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10018 }
10019
10020 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
10021                                          gpointer        data)
10022 {
10023         Compose *compose = (Compose *)data;
10024         compose->autoindent = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10025 }
10026
10027 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
10028 {
10029         Compose *compose = (Compose *)data;
10030
10031         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10032 }
10033
10034 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10035 {
10036         Compose *compose = (Compose *)data;
10037
10038         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10039 }
10040
10041 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
10042 {
10043         g_free(compose->privacy_system);
10044
10045         compose->privacy_system = g_strdup(account->default_privacy_system);
10046         compose_update_privacy_system_menu_item(compose, warn);
10047 }
10048
10049 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10050 {
10051         Compose *compose = (Compose *)data;
10052
10053         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10054                 gtk_widget_show(compose->ruler_hbox);
10055                 prefs_common.show_ruler = TRUE;
10056         } else {
10057                 gtk_widget_hide(compose->ruler_hbox);
10058                 gtk_widget_queue_resize(compose->edit_vbox);
10059                 prefs_common.show_ruler = FALSE;
10060         }
10061 }
10062
10063 static void compose_attach_drag_received_cb (GtkWidget          *widget,
10064                                              GdkDragContext     *context,
10065                                              gint                x,
10066                                              gint                y,
10067                                              GtkSelectionData   *data,
10068                                              guint               info,
10069                                              guint               time,
10070                                              gpointer            user_data)
10071 {
10072         Compose *compose = (Compose *)user_data;
10073         GList *list, *tmp;
10074
10075         if (gdk_atom_name(data->type) && 
10076             !strcmp(gdk_atom_name(data->type), "text/uri-list")
10077             && gtk_drag_get_source_widget(context) != 
10078                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10079                 list = uri_list_extract_filenames((const gchar *)data->data);
10080                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10081                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10082                         compose_attach_append
10083                                 (compose, (const gchar *)tmp->data,
10084                                  utf8_filename, NULL);
10085                         g_free(utf8_filename);
10086                 }
10087                 if (list) compose_changed_cb(NULL, compose);
10088                 list_free_strings(list);
10089                 g_list_free(list);
10090         } else if (gtk_drag_get_source_widget(context) 
10091                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10092                 /* comes from our summaryview */
10093                 SummaryView * summaryview = NULL;
10094                 GSList * list = NULL, *cur = NULL;
10095                 
10096                 if (mainwindow_get_mainwindow())
10097                         summaryview = mainwindow_get_mainwindow()->summaryview;
10098                 
10099                 if (summaryview)
10100                         list = summary_get_selected_msg_list(summaryview);
10101                 
10102                 for (cur = list; cur; cur = cur->next) {
10103                         MsgInfo *msginfo = (MsgInfo *)cur->data;
10104                         gchar *file = NULL;
10105                         if (msginfo)
10106                                 file = procmsg_get_message_file_full(msginfo, 
10107                                         TRUE, TRUE);
10108                         if (file) {
10109                                 compose_attach_append(compose, (const gchar *)file, 
10110                                         (const gchar *)file, "message/rfc822");
10111                                 g_free(file);
10112                         }
10113                 }
10114                 g_slist_free(list);
10115         }
10116 }
10117
10118 static gboolean compose_drag_drop(GtkWidget *widget,
10119                                   GdkDragContext *drag_context,
10120                                   gint x, gint y,
10121                                   guint time, gpointer user_data)
10122 {
10123         /* not handling this signal makes compose_insert_drag_received_cb
10124          * called twice */
10125         return TRUE;                                     
10126 }
10127
10128 static void compose_insert_drag_received_cb (GtkWidget          *widget,
10129                                              GdkDragContext     *drag_context,
10130                                              gint                x,
10131                                              gint                y,
10132                                              GtkSelectionData   *data,
10133                                              guint               info,
10134                                              guint               time,
10135                                              gpointer            user_data)
10136 {
10137         Compose *compose = (Compose *)user_data;
10138         GList *list, *tmp;
10139
10140         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10141          * does not work */
10142         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10143                 AlertValue val = G_ALERTDEFAULT;
10144
10145                 list = uri_list_extract_filenames((const gchar *)data->data);
10146
10147                 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10148                         /* Assume a list of no files, and data has ://, is a remote link */
10149                         gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10150                         gchar *tmpfile = get_tmp_file();
10151                         str_write_to_file(tmpdata, tmpfile);
10152                         g_free(tmpdata);  
10153                         compose_insert_file(compose, tmpfile);
10154                         claws_unlink(tmpfile);
10155                         g_free(tmpfile);
10156                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10157                         compose_beautify_paragraph(compose, NULL, TRUE);
10158                         return;
10159                 }
10160                 switch (prefs_common.compose_dnd_mode) {
10161                         case COMPOSE_DND_ASK:
10162                                 val = alertpanel_full(_("Insert or attach?"),
10163                                          _("Do you want to insert the contents of the file(s) "
10164                                            "into the message body, or attach it to the email?"),
10165                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10166                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10167                                 break;
10168                         case COMPOSE_DND_INSERT:
10169                                 val = G_ALERTALTERNATE;
10170                                 break;
10171                         case COMPOSE_DND_ATTACH:
10172                                 val = G_ALERTOTHER;
10173                                 break;
10174                         default:
10175                                 /* unexpected case */
10176                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10177                 }
10178
10179                 if (val & G_ALERTDISABLE) {
10180                         val &= ~G_ALERTDISABLE;
10181                         /* remember what action to perform by default, only if we don't click Cancel */
10182                         if (val == G_ALERTALTERNATE)
10183                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10184                         else if (val == G_ALERTOTHER)
10185                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10186                 }
10187
10188                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10189                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
10190                         list_free_strings(list);
10191                         g_list_free(list);
10192                         return;
10193                 } else if (val == G_ALERTOTHER) {
10194                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10195                         list_free_strings(list);
10196                         g_list_free(list);
10197                         return;
10198                 } 
10199
10200                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10201                         compose_insert_file(compose, (const gchar *)tmp->data);
10202                 }
10203                 list_free_strings(list);
10204                 g_list_free(list);
10205                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10206                 return;
10207         } else {
10208 #if GTK_CHECK_VERSION(2, 8, 0)
10209                 /* do nothing, handled by GTK */
10210 #else
10211                 gchar *tmpfile = get_tmp_file();
10212                 str_write_to_file((const gchar *)data->data, tmpfile);
10213                 compose_insert_file(compose, tmpfile);
10214                 claws_unlink(tmpfile);
10215                 g_free(tmpfile);
10216                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10217 #endif
10218                 return;
10219         }
10220         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10221 }
10222
10223 static void compose_header_drag_received_cb (GtkWidget          *widget,
10224                                              GdkDragContext     *drag_context,
10225                                              gint                x,
10226                                              gint                y,
10227                                              GtkSelectionData   *data,
10228                                              guint               info,
10229                                              guint               time,
10230                                              gpointer            user_data)
10231 {
10232         GtkEditable *entry = (GtkEditable *)user_data;
10233         gchar *email = (gchar *)data->data;
10234
10235         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10236          * does not work */
10237
10238         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10239                 gchar *decoded=g_new(gchar, strlen(email));
10240                 int start = 0;
10241
10242                 email += strlen("mailto:");
10243                 decode_uri(decoded, email); /* will fit */
10244                 gtk_editable_delete_text(entry, 0, -1);
10245                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10246                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10247                 g_free(decoded);
10248                 return;
10249         }
10250         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10251 }
10252
10253 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10254 {
10255         Compose *compose = (Compose *)data;
10256
10257         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10258                 compose->return_receipt = TRUE;
10259         else
10260                 compose->return_receipt = FALSE;
10261 }
10262
10263 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10264 {
10265         Compose *compose = (Compose *)data;
10266
10267         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10268                 compose->remove_references = TRUE;
10269         else
10270                 compose->remove_references = FALSE;
10271 }
10272
10273 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10274                                             GdkEventKey *event,
10275                                             ComposeHeaderEntry *headerentry)
10276 {
10277         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10278             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10279             !(event->state & GDK_MODIFIER_MASK) &&
10280             (event->keyval == GDK_BackSpace) &&
10281             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10282                 gtk_container_remove
10283                         (GTK_CONTAINER(headerentry->compose->header_table),
10284                          headerentry->combo);
10285                 gtk_container_remove
10286                         (GTK_CONTAINER(headerentry->compose->header_table),
10287                          headerentry->entry);
10288                 headerentry->compose->header_list =
10289                         g_slist_remove(headerentry->compose->header_list,
10290                                        headerentry);
10291                 g_free(headerentry);
10292         } else  if (event->keyval == GDK_Tab) {
10293                 if (headerentry->compose->header_last == headerentry) {
10294                         /* Override default next focus, and give it to subject_entry
10295                          * instead of notebook tabs
10296                          */
10297                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
10298                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
10299                         return TRUE;
10300                 }
10301         }
10302         return FALSE;
10303 }
10304
10305 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10306                                     ComposeHeaderEntry *headerentry)
10307 {
10308         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10309                 compose_create_header_entry(headerentry->compose);
10310                 g_signal_handlers_disconnect_matched
10311                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10312                          0, 0, NULL, NULL, headerentry);
10313                 
10314                 /* Automatically scroll down */
10315                 compose_show_first_last_header(headerentry->compose, FALSE);
10316                 
10317         }
10318         return FALSE;
10319 }
10320
10321 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10322 {
10323         GtkAdjustment *vadj;
10324
10325         g_return_if_fail(compose);
10326         g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10327         g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10328
10329         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10330         gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10331         gtk_adjustment_changed(vadj);
10332 }
10333
10334 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10335                           const gchar *text, gint len, Compose *compose)
10336 {
10337         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10338                                 (G_OBJECT(compose->text), "paste_as_quotation"));
10339         GtkTextMark *mark;
10340
10341         g_return_if_fail(text != NULL);
10342
10343         g_signal_handlers_block_by_func(G_OBJECT(buffer),
10344                                         G_CALLBACK(text_inserted),
10345                                         compose);
10346         if (paste_as_quotation) {
10347                 gchar *new_text;
10348                 const gchar *qmark;
10349                 guint pos = 0;
10350                 GtkTextIter start_iter;
10351
10352                 if (len < 0)
10353                         len = strlen(text);
10354
10355                 new_text = g_strndup(text, len);
10356
10357                 qmark = compose_quote_char_from_context(compose);
10358
10359                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10360                 gtk_text_buffer_place_cursor(buffer, iter);
10361
10362                 pos = gtk_text_iter_get_offset(iter);
10363
10364                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10365                                                   _("Quote format error at line %d."));
10366                 quote_fmt_reset_vartable();
10367                 g_free(new_text);
10368                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10369                                   GINT_TO_POINTER(paste_as_quotation - 1));
10370                                   
10371                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10372                 gtk_text_buffer_place_cursor(buffer, iter);
10373                 gtk_text_buffer_delete_mark(buffer, mark);
10374
10375                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10376                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10377                 compose_beautify_paragraph(compose, &start_iter, FALSE);
10378                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10379                 gtk_text_buffer_delete_mark(buffer, mark);
10380         } else {
10381                 if (strcmp(text, "\n") || compose->automatic_break
10382                 || gtk_text_iter_starts_line(iter)) {
10383                         GtkTextIter before_ins;
10384                         gtk_text_buffer_insert(buffer, iter, text, len);
10385                         if (!strstr(text, "\n") && gtk_text_iter_has_tag(iter, compose->no_join_tag)) {
10386                                 before_ins = *iter; 
10387                                 gtk_text_iter_backward_chars(&before_ins, len);
10388                                 gtk_text_buffer_remove_tag_by_name(buffer, "no_join", &before_ins, iter);
10389                         }
10390                 } else {
10391                         /* check if the preceding is just whitespace or quote */
10392                         GtkTextIter start_line;
10393                         gchar *tmp = NULL, *quote = NULL;
10394                         gint quote_len = 0, is_normal = 0;
10395                         start_line = *iter;
10396                         gtk_text_iter_set_line_offset(&start_line, 0); 
10397                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10398                         g_strstrip(tmp);
10399
10400                         if (*tmp == '\0') {
10401                                 is_normal = 1;
10402                         } else {
10403                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
10404                                 if (quote)
10405                                         is_normal = 1;
10406                                 g_free(quote);
10407                         }
10408                         g_free(tmp);
10409                         
10410                         if (is_normal) {
10411                                 gtk_text_buffer_insert(buffer, iter, text, len);
10412                         } else {
10413                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
10414                                         iter, text, len, "no_join", NULL);
10415                         }
10416                 }
10417         }
10418         
10419         if (!paste_as_quotation) {
10420                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10421                 compose_beautify_paragraph(compose, iter, FALSE);
10422                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10423                 gtk_text_buffer_delete_mark(buffer, mark);
10424         }
10425
10426         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10427                                           G_CALLBACK(text_inserted),
10428                                           compose);
10429         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10430
10431         if (prefs_common.autosave && 
10432             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10433             compose->draft_timeout_tag != -2 /* disabled while loading */)
10434                 compose->draft_timeout_tag = g_timeout_add
10435                         (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10436 }
10437 static gint compose_defer_auto_save_draft(Compose *compose)
10438 {
10439         compose->draft_timeout_tag = -1;
10440         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10441         return FALSE;
10442 }
10443
10444 #if USE_ASPELL
10445 static void compose_check_all(GtkAction *action, gpointer data)
10446 {
10447         Compose *compose = (Compose *)data;
10448         if (compose->gtkaspell)
10449                 gtkaspell_check_all(compose->gtkaspell);
10450 }
10451
10452 static void compose_highlight_all(GtkAction *action, gpointer data)
10453 {
10454         Compose *compose = (Compose *)data;
10455         if (compose->gtkaspell)
10456                 gtkaspell_highlight_all(compose->gtkaspell);
10457 }
10458
10459 static void compose_check_backwards(GtkAction *action, gpointer data)
10460 {
10461         Compose *compose = (Compose *)data;
10462         if (compose->gtkaspell) 
10463                 gtkaspell_check_backwards(compose->gtkaspell);
10464         else {
10465                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10466         }
10467 }
10468
10469 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10470 {
10471         Compose *compose = (Compose *)data;
10472         if (compose->gtkaspell) 
10473                 gtkaspell_check_forwards_go(compose->gtkaspell);
10474         else {
10475                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10476         }
10477 }
10478 #endif
10479
10480 /*!
10481  *\brief        Guess originating forward account from MsgInfo and several 
10482  *              "common preference" settings. Return NULL if no guess. 
10483  */
10484 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10485 {
10486         PrefsAccount *account = NULL;
10487         
10488         g_return_val_if_fail(msginfo, NULL);
10489         g_return_val_if_fail(msginfo->folder, NULL);
10490         g_return_val_if_fail(msginfo->folder->prefs, NULL);
10491
10492         if (msginfo->folder->prefs->enable_default_account)
10493                 account = account_find_from_id(msginfo->folder->prefs->default_account);
10494                 
10495         if (!account) 
10496                 account = msginfo->folder->folder->account;
10497                 
10498         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10499                 gchar *to;
10500                 Xstrdup_a(to, msginfo->to, return NULL);
10501                 extract_address(to);
10502                 account = account_find_from_address(to, FALSE);
10503         }
10504
10505         if (!account && prefs_common.forward_account_autosel) {
10506                 gchar cc[BUFFSIZE];
10507                 if (!procheader_get_header_from_msginfo
10508                         (msginfo, cc,sizeof cc , "Cc:")) { 
10509                         gchar *buf = cc + strlen("Cc:");
10510                         extract_address(buf);
10511                         account = account_find_from_address(buf, FALSE);
10512                 }
10513         }
10514         
10515         if (!account && prefs_common.forward_account_autosel) {
10516                 gchar deliveredto[BUFFSIZE];
10517                 if (!procheader_get_header_from_msginfo
10518                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
10519                         gchar *buf = deliveredto + strlen("Delivered-To:");
10520                         extract_address(buf);
10521                         account = account_find_from_address(buf, FALSE);
10522                 }
10523         }
10524         
10525         return account;
10526 }
10527
10528 gboolean compose_close(Compose *compose)
10529 {
10530         gint x, y;
10531
10532         if (!g_mutex_trylock(compose->mutex)) {
10533                 /* we have to wait for the (possibly deferred by auto-save)
10534                  * drafting to be done, before destroying the compose under
10535                  * it. */
10536                 debug_print("waiting for drafting to finish...\n");
10537                 compose_allow_user_actions(compose, FALSE);
10538                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10539                 return FALSE;
10540         }
10541         g_return_val_if_fail(compose, FALSE);
10542         gtkut_widget_get_uposition(compose->window, &x, &y);
10543         prefs_common.compose_x = x;
10544         prefs_common.compose_y = y;
10545         g_mutex_unlock(compose->mutex);
10546         compose_destroy(compose);
10547         return FALSE;
10548 }
10549
10550 /**
10551  * Add entry field for each address in list.
10552  * \param compose     E-Mail composition object.
10553  * \param listAddress List of (formatted) E-Mail addresses.
10554  */
10555 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10556         GList *node;
10557         gchar *addr;
10558         node = listAddress;
10559         while( node ) {
10560                 addr = ( gchar * ) node->data;
10561                 compose_entry_append( compose, addr, COMPOSE_TO );
10562                 node = g_list_next( node );
10563         }
10564 }
10565
10566 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
10567                                     guint action, gboolean opening_multiple)
10568 {
10569         gchar *body = NULL;
10570         GSList *new_msglist = NULL;
10571         MsgInfo *tmp_msginfo = NULL;
10572         gboolean originally_enc = FALSE;
10573         gboolean originally_sig = FALSE;
10574         Compose *compose = NULL;
10575
10576         g_return_if_fail(msgview != NULL);
10577
10578         g_return_if_fail(msginfo_list != NULL);
10579
10580         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10581                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10582                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10583
10584                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
10585                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10586                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10587                                                 orig_msginfo, mimeinfo);
10588                         if (tmp_msginfo != NULL) {
10589                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
10590
10591                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10592                                 privacy_msginfo_get_signed_state(orig_msginfo);
10593                                 originally_sig = MSG_IS_SIGNED(orig_msginfo->flags);
10594
10595                                 tmp_msginfo->folder = orig_msginfo->folder;
10596                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
10597                                 if (orig_msginfo->tags)
10598                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10599                         }
10600                 }
10601         }
10602
10603         if (!opening_multiple)
10604                 body = messageview_get_selection(msgview);
10605
10606         if (new_msglist) {
10607                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10608                 procmsg_msginfo_free(tmp_msginfo);
10609                 g_slist_free(new_msglist);
10610         } else
10611                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10612
10613         if (compose && originally_enc) {
10614                 compose_force_encryption(compose, compose->account, FALSE);
10615         }
10616
10617         if (compose && originally_sig && compose->account->default_sign_reply) {
10618                 compose_force_signing(compose, compose->account);
10619         }
10620
10621         g_free(body);
10622 }
10623
10624 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
10625                                     guint action)
10626 {
10627         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
10628         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10629                 GSList *cur = msginfo_list;
10630                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10631                                                "messages. Opening the windows "
10632                                                "could take some time. Do you "
10633                                                "want to continue?"), 
10634                                                g_slist_length(msginfo_list));
10635                 if (g_slist_length(msginfo_list) > 9
10636                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10637                     != G_ALERTALTERNATE) {
10638                         g_free(msg);
10639                         return;
10640                 }
10641                 g_free(msg);
10642                 /* We'll open multiple compose windows */
10643                 /* let the WM place the next windows */
10644                 compose_force_window_origin = FALSE;
10645                 for (; cur; cur = cur->next) {
10646                         GSList tmplist;
10647                         tmplist.data = cur->data;
10648                         tmplist.next = NULL;
10649                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10650                 }
10651                 compose_force_window_origin = TRUE;
10652         } else {
10653                 /* forwarding multiple mails as attachments is done via a
10654                  * single compose window */
10655                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10656         }
10657 }
10658
10659 void compose_set_position(Compose *compose, gint pos)
10660 {
10661         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10662
10663         gtkut_text_view_set_position(text, pos);
10664 }
10665
10666 gboolean compose_search_string(Compose *compose,
10667                                 const gchar *str, gboolean case_sens)
10668 {
10669         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10670
10671         return gtkut_text_view_search_string(text, str, case_sens);
10672 }
10673
10674 gboolean compose_search_string_backward(Compose *compose,
10675                                 const gchar *str, gboolean case_sens)
10676 {
10677         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10678
10679         return gtkut_text_view_search_string_backward(text, str, case_sens);
10680 }
10681
10682 /* allocate a msginfo structure and populate its data from a compose data structure */
10683 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10684 {
10685         MsgInfo *newmsginfo;
10686         GSList *list;
10687         gchar buf[BUFFSIZE];
10688
10689         g_return_val_if_fail( compose != NULL, NULL );
10690
10691         newmsginfo = procmsg_msginfo_new();
10692
10693         /* date is now */
10694         get_rfc822_date(buf, sizeof(buf));
10695         newmsginfo->date = g_strdup(buf);
10696
10697         /* from */
10698         if (compose->from_name) {
10699                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10700                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10701         }
10702
10703         /* subject */
10704         if (compose->subject_entry)
10705                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10706
10707         /* to, cc, reply-to, newsgroups */
10708         for (list = compose->header_list; list; list = list->next) {
10709                 gchar *header = gtk_editable_get_chars(
10710                                                                 GTK_EDITABLE(
10711                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10712                 gchar *entry = gtk_editable_get_chars(
10713                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10714
10715                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10716                         if ( newmsginfo->to == NULL ) {
10717                                 newmsginfo->to = g_strdup(entry);
10718                         } else if (entry && *entry) {
10719                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10720                                 g_free(newmsginfo->to);
10721                                 newmsginfo->to = tmp;
10722                         }
10723                 } else
10724                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10725                         if ( newmsginfo->cc == NULL ) {
10726                                 newmsginfo->cc = g_strdup(entry);
10727                         } else if (entry && *entry) {
10728                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10729                                 g_free(newmsginfo->cc);
10730                                 newmsginfo->cc = tmp;
10731                         }
10732                 } else
10733                 if ( strcasecmp(header,
10734                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10735                         if ( newmsginfo->newsgroups == NULL ) {
10736                                 newmsginfo->newsgroups = g_strdup(entry);
10737                         } else if (entry && *entry) {
10738                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10739                                 g_free(newmsginfo->newsgroups);
10740                                 newmsginfo->newsgroups = tmp;
10741                         }
10742                 }
10743
10744                 g_free(header);
10745                 g_free(entry);  
10746         }
10747
10748         /* other data is unset */
10749
10750         return newmsginfo;
10751 }
10752
10753 #ifdef USE_ASPELL
10754 /* update compose's dictionaries from folder dict settings */
10755 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10756                                                 FolderItem *folder_item)
10757 {
10758         g_return_if_fail(compose != NULL);
10759
10760         if (compose->gtkaspell && folder_item && folder_item->prefs) {
10761                 FolderItemPrefs *prefs = folder_item->prefs;
10762
10763                 if (prefs->enable_default_dictionary)
10764                         gtkaspell_change_dict(compose->gtkaspell,
10765                                         prefs->default_dictionary, FALSE);
10766                 if (folder_item->prefs->enable_default_alt_dictionary)
10767                         gtkaspell_change_alt_dict(compose->gtkaspell,
10768                                         prefs->default_alt_dictionary);
10769                 if (prefs->enable_default_dictionary
10770                         || prefs->enable_default_alt_dictionary)
10771                         compose_spell_menu_changed(compose);
10772         }
10773 }
10774 #endif
10775
10776 /*
10777  * End of Source.
10778  */