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