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