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