2008-08-21 [colin] 3.5.0cvs65
[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 >0 if the line is itemized */
3666 static int compose_itemized_length(GtkTextBuffer *buffer,
3667                                     const GtkTextIter *start)
3668 {
3669         GtkTextIter iter = *start;
3670         gunichar wc;
3671         gchar ch[6];
3672         gint clen;
3673         gint len = 0;
3674         if (gtk_text_iter_ends_line(&iter))
3675                 return 0;
3676
3677         while (1) {
3678                 len++;
3679                 wc = gtk_text_iter_get_char(&iter);
3680                 if (!g_unichar_isspace(wc))
3681                         break;
3682                 gtk_text_iter_forward_char(&iter);
3683                 if (gtk_text_iter_ends_line(&iter))
3684                         return 0;
3685         }
3686
3687         clen = g_unichar_to_utf8(wc, ch);
3688         if (clen != 1)
3689                 return 0;
3690
3691         if (!strchr("*-+", ch[0]))
3692                 return 0;
3693
3694         gtk_text_iter_forward_char(&iter);
3695         if (gtk_text_iter_ends_line(&iter))
3696                 return 0;
3697         wc = gtk_text_iter_get_char(&iter);
3698         if (g_unichar_isspace(wc)) {
3699                 return len+1;
3700         }
3701         return 0;
3702 }
3703
3704 /* return the string at the start of the itemization */
3705 static gchar * compose_get_itemized_chars(GtkTextBuffer *buffer,
3706                                     const GtkTextIter *start)
3707 {
3708         GtkTextIter iter = *start;
3709         gunichar wc;
3710         gint len = 0;
3711         GString *item_chars = g_string_new("");
3712         gchar *str = NULL;
3713
3714         if (gtk_text_iter_ends_line(&iter))
3715                 return NULL;
3716
3717         while (1) {
3718                 len++;
3719                 wc = gtk_text_iter_get_char(&iter);
3720                 if (!g_unichar_isspace(wc))
3721                         break;
3722                 gtk_text_iter_forward_char(&iter);
3723                 if (gtk_text_iter_ends_line(&iter))
3724                         break;
3725                 g_string_append_unichar(item_chars, wc);
3726         }
3727
3728         str = item_chars->str;
3729         g_string_free(item_chars, FALSE);
3730         return str;
3731 }
3732
3733 /* return the number of spaces at a line's start */
3734 static int compose_left_offset_length(GtkTextBuffer *buffer,
3735                                     const GtkTextIter *start)
3736 {
3737         GtkTextIter iter = *start;
3738         gunichar wc;
3739         gint len = 0;
3740         if (gtk_text_iter_ends_line(&iter))
3741                 return 0;
3742
3743         while (1) {
3744                 wc = gtk_text_iter_get_char(&iter);
3745                 if (!g_unichar_isspace(wc))
3746                         break;
3747                 len++;
3748                 gtk_text_iter_forward_char(&iter);
3749                 if (gtk_text_iter_ends_line(&iter))
3750                         return 0;
3751         }
3752
3753         gtk_text_iter_forward_char(&iter);
3754         if (gtk_text_iter_ends_line(&iter))
3755                 return 0;
3756         return len;
3757 }
3758
3759 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3760                                            const GtkTextIter *start,
3761                                            GtkTextIter *break_pos,
3762                                            gint max_col,
3763                                            gint quote_len)
3764 {
3765         GtkTextIter iter = *start, line_end = *start;
3766         PangoLogAttr *attrs;
3767         gchar *str;
3768         gchar *p;
3769         gint len;
3770         gint i;
3771         gint col = 0;
3772         gint pos = 0;
3773         gboolean can_break = FALSE;
3774         gboolean do_break = FALSE;
3775         gboolean was_white = FALSE;
3776         gboolean prev_dont_break = FALSE;
3777
3778         gtk_text_iter_forward_to_line_end(&line_end);
3779         str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3780         len = g_utf8_strlen(str, -1);
3781         
3782         if (len == 0) {
3783                 g_free(str);
3784                 g_warning("compose_get_line_break_pos: len = 0!\n");
3785                 return FALSE;
3786         }
3787
3788         /* g_print("breaking line: %d: %s (len = %d)\n",
3789                 gtk_text_iter_get_line(&iter), str, len); */
3790
3791         attrs = g_new(PangoLogAttr, len + 1);
3792
3793         pango_default_break(str, -1, NULL, attrs, len + 1);
3794
3795         p = str;
3796
3797         /* skip quote and leading spaces */
3798         for (i = 0; *p != '\0' && i < len; i++) {
3799                 gunichar wc;
3800
3801                 wc = g_utf8_get_char(p);
3802                 if (i >= quote_len && !g_unichar_isspace(wc))
3803                         break;
3804                 if (g_unichar_iswide(wc))
3805                         col += 2;
3806                 else if (*p == '\t')
3807                         col += 8;
3808                 else
3809                         col++;
3810                 p = g_utf8_next_char(p);
3811         }
3812
3813         for (; *p != '\0' && i < len; i++) {
3814                 PangoLogAttr *attr = attrs + i;
3815                 gunichar wc;
3816                 gint uri_len;
3817
3818                 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3819                         pos = i;
3820                 
3821                 was_white = attr->is_white;
3822
3823                 /* don't wrap URI */
3824                 if ((uri_len = get_uri_len(p)) > 0) {
3825                         col += uri_len;
3826                         if (pos > 0 && col > max_col) {
3827                                 do_break = TRUE;
3828                                 break;
3829                         }
3830                         i += uri_len - 1;
3831                         p += uri_len;
3832                         can_break = TRUE;
3833                         continue;
3834                 }
3835
3836                 wc = g_utf8_get_char(p);
3837                 if (g_unichar_iswide(wc)) {
3838                         col += 2;
3839                         if (prev_dont_break && can_break && attr->is_line_break)
3840                                 pos = i;
3841                 } else if (*p == '\t')
3842                         col += 8;
3843                 else
3844                         col++;
3845                 if (pos > 0 && col > max_col) {
3846                         do_break = TRUE;
3847                         break;
3848                 }
3849
3850                 if (*p == '-' || *p == '/')
3851                         prev_dont_break = TRUE;
3852                 else
3853                         prev_dont_break = FALSE;
3854
3855                 p = g_utf8_next_char(p);
3856                 can_break = TRUE;
3857         }
3858
3859         debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3860
3861         g_free(attrs);
3862         g_free(str);
3863
3864         *break_pos = *start;
3865         gtk_text_iter_set_line_offset(break_pos, pos);
3866
3867         return do_break;
3868 }
3869
3870 static gboolean compose_join_next_line(Compose *compose,
3871                                        GtkTextBuffer *buffer,
3872                                        GtkTextIter *iter,
3873                                        const gchar *quote_str)
3874 {
3875         GtkTextIter iter_ = *iter, cur, prev, next, end;
3876         PangoLogAttr attrs[3];
3877         gchar *str;
3878         gchar *next_quote_str;
3879         gunichar wc1, wc2;
3880         gint quote_len;
3881         gboolean keep_cursor = FALSE;
3882
3883         if (!gtk_text_iter_forward_line(&iter_) ||
3884             gtk_text_iter_ends_line(&iter_)) {
3885                 return FALSE;
3886         }
3887         next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
3888
3889         if ((quote_str || next_quote_str) &&
3890             strcmp2(quote_str, next_quote_str) != 0) {
3891                 g_free(next_quote_str);
3892                 return FALSE;
3893         }
3894         g_free(next_quote_str);
3895
3896         end = iter_;
3897         if (quote_len > 0) {
3898                 gtk_text_iter_forward_chars(&end, quote_len);
3899                 if (gtk_text_iter_ends_line(&end)) {
3900                         return FALSE;
3901                 }
3902         }
3903
3904         /* don't join itemized lines */
3905         if (compose_itemized_length(buffer, &end) > 0) {
3906                 return FALSE;
3907         }
3908
3909         /* don't join signature separator */
3910         if (compose_is_sig_separator(compose, buffer, &iter_)) {
3911                 return FALSE;
3912         }
3913         /* delete quote str */
3914         if (quote_len > 0)
3915                 gtk_text_buffer_delete(buffer, &iter_, &end);
3916
3917         /* don't join line breaks put by the user */
3918         prev = cur = iter_;
3919         gtk_text_iter_backward_char(&cur);
3920         if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3921                 gtk_text_iter_forward_char(&cur);
3922                 *iter = cur;
3923                 return FALSE;
3924         }
3925         gtk_text_iter_forward_char(&cur);
3926         /* delete linebreak and extra spaces */
3927         while (gtk_text_iter_backward_char(&cur)) {
3928                 wc1 = gtk_text_iter_get_char(&cur);
3929                 if (!g_unichar_isspace(wc1))
3930                         break;
3931                 prev = cur;
3932         }
3933         next = cur = iter_;
3934         while (!gtk_text_iter_ends_line(&cur)) {
3935                 wc1 = gtk_text_iter_get_char(&cur);
3936                 if (!g_unichar_isspace(wc1))
3937                         break;
3938                 gtk_text_iter_forward_char(&cur);
3939                 next = cur;
3940         }
3941         if (!gtk_text_iter_equal(&prev, &next)) {
3942                 GtkTextMark *mark;
3943
3944                 mark = gtk_text_buffer_get_insert(buffer);
3945                 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3946                 if (gtk_text_iter_equal(&prev, &cur))
3947                         keep_cursor = TRUE;
3948                 gtk_text_buffer_delete(buffer, &prev, &next);
3949         }
3950         iter_ = prev;
3951
3952         /* insert space if required */
3953         gtk_text_iter_backward_char(&prev);
3954         wc1 = gtk_text_iter_get_char(&prev);
3955         wc2 = gtk_text_iter_get_char(&next);
3956         gtk_text_iter_forward_char(&next);
3957         str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3958         pango_default_break(str, -1, NULL, attrs, 3);
3959         if (!attrs[1].is_line_break ||
3960             (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3961                 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3962                 if (keep_cursor) {
3963                         gtk_text_iter_backward_char(&iter_);
3964                         gtk_text_buffer_place_cursor(buffer, &iter_);
3965                 }
3966         }
3967         g_free(str);
3968
3969         *iter = iter_;
3970         return TRUE;
3971 }
3972
3973 #define ADD_TXT_POS(bp_, ep_, pti_) \
3974         if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3975                 last = last->next; \
3976                 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3977                 last->next = NULL; \
3978         } else { \
3979                 g_warning("alloc error scanning URIs\n"); \
3980         }
3981
3982 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3983 {
3984         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3985         GtkTextBuffer *buffer;
3986         GtkTextIter iter, break_pos, end_of_line;
3987         gchar *quote_str = NULL;
3988         gint quote_len;
3989         gboolean wrap_quote = prefs_common.linewrap_quote;
3990         gboolean prev_autowrap = compose->autowrap;
3991         gint startq_offset = -1, noq_offset = -1;
3992         gint uri_start = -1, uri_stop = -1;
3993         gint nouri_start = -1, nouri_stop = -1;
3994         gint num_blocks = 0;
3995         gint quotelevel = -1;
3996         gboolean modified = force;
3997         gboolean removed = FALSE;
3998         gboolean modified_before_remove = FALSE;
3999         gint lines = 0;
4000         gboolean start = TRUE;
4001         gint itemized_len = 0, rem_item_len = 0;
4002         gchar *itemized_chars = NULL;
4003         gboolean item_continuation = FALSE;
4004
4005         if (force) {
4006                 modified = TRUE;
4007         }
4008         if (compose->draft_timeout_tag == -2) {
4009                 modified = TRUE;
4010         }
4011
4012         compose->autowrap = FALSE;
4013
4014         buffer = gtk_text_view_get_buffer(text);
4015         undo_wrapping(compose->undostruct, TRUE);
4016         if (par_iter) {
4017                 iter = *par_iter;
4018         } else {
4019                 GtkTextMark *mark;
4020                 mark = gtk_text_buffer_get_insert(buffer);
4021                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
4022         }
4023
4024
4025         if (compose->draft_timeout_tag == -2) {
4026                 if (gtk_text_iter_ends_line(&iter)) {
4027                         while (gtk_text_iter_ends_line(&iter) &&
4028                                gtk_text_iter_forward_line(&iter))
4029                                 ;
4030                 } else {
4031                         while (gtk_text_iter_backward_line(&iter)) {
4032                                 if (gtk_text_iter_ends_line(&iter)) {
4033                                         gtk_text_iter_forward_line(&iter);
4034                                         break;
4035                                 }
4036                         }
4037                 }
4038         } else {
4039                 /* move to line start */
4040                 gtk_text_iter_set_line_offset(&iter, 0);
4041         }
4042         
4043         itemized_len = compose_itemized_length(buffer, &iter);
4044         
4045         if (!itemized_len) {
4046                 itemized_len = compose_left_offset_length(buffer, &iter);
4047                 item_continuation = TRUE;
4048         }
4049
4050         if (itemized_len)
4051                 itemized_chars = compose_get_itemized_chars(buffer, &iter);
4052
4053         /* go until paragraph end (empty line) */
4054         while (start || !gtk_text_iter_ends_line(&iter)) {
4055                 gchar *scanpos = NULL;
4056                 /* parse table - in order of priority */
4057                 struct table {
4058                         const gchar *needle; /* token */
4059
4060                         /* token search function */
4061                         gchar    *(*search)     (const gchar *haystack,
4062                                                  const gchar *needle);
4063                         /* part parsing function */
4064                         gboolean  (*parse)      (const gchar *start,
4065                                                  const gchar *scanpos,
4066                                                  const gchar **bp_,
4067                                                  const gchar **ep_,
4068                                                  gboolean hdr);
4069                         /* part to URI function */
4070                         gchar    *(*build_uri)  (const gchar *bp,
4071                                                  const gchar *ep);
4072                 };
4073
4074                 static struct table parser[] = {
4075                         {"http://",  strcasestr, get_uri_part,   make_uri_string},
4076                         {"https://", strcasestr, get_uri_part,   make_uri_string},
4077                         {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
4078                         {"sftp://",  strcasestr, get_uri_part,   make_uri_string},
4079                         {"www.",     strcasestr, get_uri_part,   make_http_string},
4080                         {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
4081                         {"@",        strcasestr, get_email_part, make_email_string}
4082                 };
4083                 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
4084                 gint last_index = PARSE_ELEMS;
4085                 gint  n;
4086                 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
4087                 gint walk_pos;
4088                 
4089                 start = FALSE;
4090                 if (!prev_autowrap && num_blocks == 0) {
4091                         num_blocks++;
4092                         g_signal_handlers_block_by_func(G_OBJECT(buffer),
4093                                         G_CALLBACK(text_inserted),
4094                                         compose);
4095                 }
4096                 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
4097                         goto colorize;
4098
4099                 uri_start = uri_stop = -1;
4100                 quote_len = 0;
4101                 quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
4102
4103                 if (quote_str) {
4104                         debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
4105                         if (startq_offset == -1) 
4106                                 startq_offset = gtk_text_iter_get_offset(&iter);
4107                         quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
4108                         if (quotelevel > 2) {
4109                                 /* recycle colors */
4110                                 if (prefs_common.recycle_quote_colors)
4111                                         quotelevel %= 3;
4112                                 else
4113                                         quotelevel = 2;
4114                         }
4115                         if (!wrap_quote) {
4116                                 goto colorize;
4117                         }
4118                 } else {
4119                         if (startq_offset == -1)
4120                                 noq_offset = gtk_text_iter_get_offset(&iter);
4121                         quotelevel = -1;
4122                 }
4123
4124                 if (prev_autowrap == FALSE && !force && !wrap_quote) {
4125                         goto colorize;
4126                 }
4127                 if (gtk_text_iter_ends_line(&iter)) {
4128                         goto colorize;
4129                 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
4130                                                prefs_common.linewrap_len,
4131                                                quote_len)) {
4132                         GtkTextIter prev, next, cur;
4133                         if (prev_autowrap != FALSE || force) {
4134                                 compose->automatic_break = TRUE;
4135                                 modified = TRUE;
4136                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4137                                 compose->automatic_break = FALSE;
4138                                 if (itemized_len) {
4139                                         gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4140                                         if (!item_continuation)
4141                                                 gtk_text_buffer_insert(buffer, &break_pos, "  ", 2);
4142                                 }
4143                         } else if (quote_str && wrap_quote) {
4144                                 compose->automatic_break = TRUE;
4145                                 modified = TRUE;
4146                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4147                                 compose->automatic_break = FALSE;
4148                                 if (itemized_len) {
4149                                         gtk_text_buffer_insert(buffer, &break_pos, itemized_chars, -1);
4150                                         if (!item_continuation)
4151                                                 gtk_text_buffer_insert(buffer, &break_pos, "  ", 2);
4152                                 }
4153                         } else 
4154                                 goto colorize;
4155                         /* remove trailing spaces */
4156                         cur = break_pos;
4157                         rem_item_len = itemized_len;
4158                         while (rem_item_len-- > 0)
4159                                 gtk_text_iter_backward_char(&cur);
4160                         gtk_text_iter_backward_char(&cur);
4161
4162                         prev = next = cur;
4163                         while (!gtk_text_iter_starts_line(&cur)) {
4164                                 gunichar wc;
4165
4166                                 gtk_text_iter_backward_char(&cur);
4167                                 wc = gtk_text_iter_get_char(&cur);
4168                                 if (!g_unichar_isspace(wc))
4169                                         break;
4170                                 prev = cur;
4171                         }
4172                         if (!gtk_text_iter_equal(&prev, &next)) {
4173                                 gtk_text_buffer_delete(buffer, &prev, &next);
4174                                 break_pos = next;
4175                                 gtk_text_iter_forward_char(&break_pos);
4176                         }
4177
4178                         if (quote_str)
4179                                 gtk_text_buffer_insert(buffer, &break_pos,
4180                                                        quote_str, -1);
4181
4182                         iter = break_pos;
4183                         modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4184
4185                         /* move iter to current line start */
4186                         gtk_text_iter_set_line_offset(&iter, 0);
4187                         if (quote_str) {
4188                                 g_free(quote_str);
4189                                 quote_str = NULL;
4190                         }
4191                         continue;       
4192                 } else {
4193                         /* move iter to next line start */
4194                         iter = break_pos;
4195                         lines++;
4196                 }
4197
4198 colorize:
4199                 if (!prev_autowrap && num_blocks > 0) {
4200                         num_blocks--;
4201                         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4202                                         G_CALLBACK(text_inserted),
4203                                         compose);
4204                 }
4205                 end_of_line = iter;
4206                 while (!gtk_text_iter_ends_line(&end_of_line)) {
4207                         gtk_text_iter_forward_char(&end_of_line);
4208                 }
4209                 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4210
4211                 nouri_start = gtk_text_iter_get_offset(&iter);
4212                 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4213
4214                 walk_pos = gtk_text_iter_get_offset(&iter);
4215                 /* FIXME: this looks phony. scanning for anything in the parse table */
4216                 for (n = 0; n < PARSE_ELEMS; n++) {
4217                         gchar *tmp;
4218
4219                         tmp = parser[n].search(walk, parser[n].needle);
4220                         if (tmp) {
4221                                 if (scanpos == NULL || tmp < scanpos) {
4222                                         scanpos = tmp;
4223                                         last_index = n;
4224                                 }
4225                         }                                       
4226                 }
4227
4228                 bp = ep = 0;
4229                 if (scanpos) {
4230                         /* check if URI can be parsed */
4231                         if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4232                                         (const gchar **)&ep, FALSE)
4233                             && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4234                                         walk = ep;
4235                         } else
4236                                 walk = scanpos +
4237                                         strlen(parser[last_index].needle);
4238                 } 
4239                 if (bp && ep) {
4240                         uri_start = walk_pos + (bp - o_walk);
4241                         uri_stop  = walk_pos + (ep - o_walk);
4242                 }
4243                 g_free(o_walk);
4244                 o_walk = NULL;
4245                 gtk_text_iter_forward_line(&iter);
4246                 g_free(quote_str);
4247                 quote_str = NULL;
4248                 if (startq_offset != -1) {
4249                         GtkTextIter startquote, endquote;
4250                         gtk_text_buffer_get_iter_at_offset(
4251                                 buffer, &startquote, startq_offset);
4252                         endquote = iter;
4253
4254                         switch (quotelevel) {
4255                         case 0: 
4256                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4257                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4258                                         gtk_text_buffer_apply_tag_by_name(
4259                                                 buffer, "quote0", &startquote, &endquote);
4260                                         gtk_text_buffer_remove_tag_by_name(
4261                                                 buffer, "quote1", &startquote, &endquote);
4262                                         gtk_text_buffer_remove_tag_by_name(
4263                                                 buffer, "quote2", &startquote, &endquote);
4264                                         modified = TRUE;
4265                                 }
4266                                 break;
4267                         case 1: 
4268                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4269                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4270                                         gtk_text_buffer_apply_tag_by_name(
4271                                                 buffer, "quote1", &startquote, &endquote);
4272                                         gtk_text_buffer_remove_tag_by_name(
4273                                                 buffer, "quote0", &startquote, &endquote);
4274                                         gtk_text_buffer_remove_tag_by_name(
4275                                                 buffer, "quote2", &startquote, &endquote);
4276                                         modified = TRUE;
4277                                 }
4278                                 break;
4279                         case 2: 
4280                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4281                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4282                                         gtk_text_buffer_apply_tag_by_name(
4283                                                 buffer, "quote2", &startquote, &endquote);
4284                                         gtk_text_buffer_remove_tag_by_name(
4285                                                 buffer, "quote0", &startquote, &endquote);
4286                                         gtk_text_buffer_remove_tag_by_name(
4287                                                 buffer, "quote1", &startquote, &endquote);
4288                                         modified = TRUE;
4289                                 }
4290                                 break;
4291                         }
4292                         startq_offset = -1;
4293                 } else if (noq_offset != -1) {
4294                         GtkTextIter startnoquote, endnoquote;
4295                         gtk_text_buffer_get_iter_at_offset(
4296                                 buffer, &startnoquote, noq_offset);
4297                         endnoquote = iter;
4298
4299                         if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4300                           && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4301                             (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4302                           && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4303                             (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4304                           && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4305                                 gtk_text_buffer_remove_tag_by_name(
4306                                         buffer, "quote0", &startnoquote, &endnoquote);
4307                                 gtk_text_buffer_remove_tag_by_name(
4308                                         buffer, "quote1", &startnoquote, &endnoquote);
4309                                 gtk_text_buffer_remove_tag_by_name(
4310                                         buffer, "quote2", &startnoquote, &endnoquote);
4311                                 modified = TRUE;
4312                         }
4313                         noq_offset = -1;
4314                 }
4315                 
4316                 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4317                         GtkTextIter nouri_start_iter, nouri_end_iter;
4318                         gtk_text_buffer_get_iter_at_offset(
4319                                 buffer, &nouri_start_iter, nouri_start);
4320                         gtk_text_buffer_get_iter_at_offset(
4321                                 buffer, &nouri_end_iter, nouri_stop);
4322                         if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4323                             gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4324                                 gtk_text_buffer_remove_tag_by_name(
4325                                         buffer, "link", &nouri_start_iter, &nouri_end_iter);
4326                                 modified_before_remove = modified;
4327                                 modified = TRUE;
4328                                 removed = TRUE;
4329                         }
4330                 }
4331                 if (uri_start >= 0 && uri_stop > 0) {
4332                         GtkTextIter uri_start_iter, uri_end_iter, back;
4333                         gtk_text_buffer_get_iter_at_offset(
4334                                 buffer, &uri_start_iter, uri_start);
4335                         gtk_text_buffer_get_iter_at_offset(
4336                                 buffer, &uri_end_iter, uri_stop);
4337                         back = uri_end_iter;
4338                         gtk_text_iter_backward_char(&back);
4339                         if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4340                             !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4341                                 gtk_text_buffer_apply_tag_by_name(
4342                                         buffer, "link", &uri_start_iter, &uri_end_iter);
4343                                 modified = TRUE;
4344                                 if (removed && !modified_before_remove) {
4345                                         modified = FALSE;
4346                                 } 
4347                         }
4348                 }
4349                 if (!modified) {
4350                         debug_print("not modified, out after %d lines\n", lines);
4351                         goto end;
4352                 }
4353         }
4354         debug_print("modified, out after %d lines\n", lines);
4355 end:
4356         g_free(itemized_chars);
4357         if (par_iter)
4358                 *par_iter = iter;
4359         undo_wrapping(compose->undostruct, FALSE);
4360         compose->autowrap = prev_autowrap;
4361         
4362         return modified;
4363 }
4364
4365 void compose_action_cb(void *data)
4366 {
4367         Compose *compose = (Compose *)data;
4368         compose_wrap_all(compose);
4369 }
4370
4371 static void compose_wrap_all(Compose *compose)
4372 {
4373         compose_wrap_all_full(compose, FALSE);
4374 }
4375
4376 static void compose_wrap_all_full(Compose *compose, gboolean force)
4377 {
4378         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4379         GtkTextBuffer *buffer;
4380         GtkTextIter iter;
4381         gboolean modified = TRUE;
4382
4383         buffer = gtk_text_view_get_buffer(text);
4384
4385         gtk_text_buffer_get_start_iter(buffer, &iter);
4386         while (!gtk_text_iter_is_end(&iter) && modified)
4387                 modified = compose_beautify_paragraph(compose, &iter, force);
4388
4389 }
4390
4391 static void compose_set_title(Compose *compose)
4392 {
4393         gchar *str;
4394         gchar *edited;
4395         gchar *subject;
4396         
4397         edited = compose->modified ? _(" [Edited]") : "";
4398         
4399         subject = gtk_editable_get_chars(
4400                         GTK_EDITABLE(compose->subject_entry), 0, -1);
4401
4402 #ifndef GENERIC_UMPC
4403         if (subject && strlen(subject))
4404                 str = g_strdup_printf(_("%s - Compose message%s"),
4405                                       subject, edited); 
4406         else
4407                 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4408 #else
4409         str = g_strdup(_("Compose message"));
4410 #endif
4411
4412         gtk_window_set_title(GTK_WINDOW(compose->window), str);
4413         g_free(str);
4414         g_free(subject);
4415 }
4416
4417 /**
4418  * compose_current_mail_account:
4419  * 
4420  * Find a current mail account (the currently selected account, or the
4421  * default account, if a news account is currently selected).  If a
4422  * mail account cannot be found, display an error message.
4423  * 
4424  * Return value: Mail account, or NULL if not found.
4425  **/
4426 static PrefsAccount *
4427 compose_current_mail_account(void)
4428 {
4429         PrefsAccount *ac;
4430
4431         if (cur_account && cur_account->protocol != A_NNTP)
4432                 ac = cur_account;
4433         else {
4434                 ac = account_get_default();
4435                 if (!ac || ac->protocol == A_NNTP) {
4436                         alertpanel_error(_("Account for sending mail is not specified.\n"
4437                                            "Please select a mail account before sending."));
4438                         return NULL;
4439                 }
4440         }
4441         return ac;
4442 }
4443
4444 #define QUOTE_IF_REQUIRED(out, str)                                     \
4445 {                                                                       \
4446         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4447                 gchar *__tmp;                                           \
4448                 gint len;                                               \
4449                                                                         \
4450                 len = strlen(str) + 3;                                  \
4451                 if ((__tmp = alloca(len)) == NULL) {                    \
4452                         g_warning("can't allocate memory\n");           \
4453                         g_string_free(header, TRUE);                    \
4454                         return NULL;                                    \
4455                 }                                                       \
4456                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4457                 out = __tmp;                                            \
4458         } else {                                                        \
4459                 gchar *__tmp;                                           \
4460                                                                         \
4461                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4462                         g_warning("can't allocate memory\n");           \
4463                         g_string_free(header, TRUE);                    \
4464                         return NULL;                                    \
4465                 } else                                                  \
4466                         strcpy(__tmp, str);                             \
4467                                                                         \
4468                 out = __tmp;                                            \
4469         }                                                               \
4470 }
4471
4472 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret)                      \
4473 {                                                                       \
4474         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4475                 gchar *__tmp;                                           \
4476                 gint len;                                               \
4477                                                                         \
4478                 len = strlen(str) + 3;                                  \
4479                 if ((__tmp = alloca(len)) == NULL) {                    \
4480                         g_warning("can't allocate memory\n");           \
4481                         errret;                                         \
4482                 }                                                       \
4483                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4484                 out = __tmp;                                            \
4485         } else {                                                        \
4486                 gchar *__tmp;                                           \
4487                                                                         \
4488                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4489                         g_warning("can't allocate memory\n");           \
4490                         errret;                                         \
4491                 } else                                                  \
4492                         strcpy(__tmp, str);                             \
4493                                                                         \
4494                 out = __tmp;                                            \
4495         }                                                               \
4496 }
4497
4498 static void compose_select_account(Compose *compose, PrefsAccount *account,
4499                                    gboolean init)
4500 {
4501         gchar *from = NULL;
4502
4503         g_return_if_fail(account != NULL);
4504
4505         compose->account = account;
4506
4507         if (account->name && *account->name) {
4508                 gchar *buf;
4509                 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4510                 from = g_strdup_printf("%s <%s>",
4511                                        buf, account->address);
4512                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4513         } else {
4514                 from = g_strdup_printf("<%s>",
4515                                        account->address);
4516                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4517         }
4518
4519         g_free(from);
4520
4521         compose_set_title(compose);
4522
4523         if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4524                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", TRUE);
4525         else
4526                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", FALSE);
4527         if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4528                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", TRUE);
4529         else
4530                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", FALSE);
4531                                        
4532         activate_privacy_system(compose, account, FALSE);
4533
4534         if (!init && compose->mode != COMPOSE_REDIRECT) {
4535                 undo_block(compose->undostruct);
4536                 compose_insert_sig(compose, TRUE);
4537                 undo_unblock(compose->undostruct);
4538         }
4539
4540 #ifdef USE_ASPELL
4541         /* use account's dict info if set */
4542         if (compose->gtkaspell) {
4543                 if (account->enable_default_dictionary)
4544                         gtkaspell_change_dict(compose->gtkaspell,
4545                                         account->default_dictionary, FALSE);
4546                 if (account->enable_default_alt_dictionary)
4547                         gtkaspell_change_alt_dict(compose->gtkaspell,
4548                                         account->default_alt_dictionary);
4549                 if (account->enable_default_dictionary
4550                         || account->enable_default_alt_dictionary)
4551                         compose_spell_menu_changed(compose);
4552         }
4553 #endif
4554 }
4555
4556 gboolean compose_check_for_valid_recipient(Compose *compose) {
4557         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4558         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4559         gboolean recipient_found = FALSE;
4560         GSList *list;
4561         gchar **strptr;
4562
4563         /* free to and newsgroup list */
4564         slist_free_strings(compose->to_list);
4565         g_slist_free(compose->to_list);
4566         compose->to_list = NULL;
4567                         
4568         slist_free_strings(compose->newsgroup_list);
4569         g_slist_free(compose->newsgroup_list);
4570         compose->newsgroup_list = NULL;
4571
4572         /* search header entries for to and newsgroup entries */
4573         for (list = compose->header_list; list; list = list->next) {
4574                 gchar *header;
4575                 gchar *entry;
4576                 header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4577                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4578                 g_strstrip(entry);
4579                 g_strstrip(header);
4580                 if (entry[0] != '\0') {
4581                         for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4582                                 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4583                                         compose->to_list = address_list_append(compose->to_list, entry);
4584                                         recipient_found = TRUE;
4585                                 }
4586                         }
4587                         for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4588                                 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4589                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4590                                         recipient_found = TRUE;
4591                                 }
4592                         }
4593                 }
4594                 g_free(header);
4595                 g_free(entry);
4596         }
4597         return recipient_found;
4598 }
4599
4600 static gboolean compose_check_for_set_recipients(Compose *compose)
4601 {
4602         if (compose->account->set_autocc && compose->account->auto_cc) {
4603                 gboolean found_other = FALSE;
4604                 GSList *list;
4605                 /* search header entries for to and newsgroup entries */
4606                 for (list = compose->header_list; list; list = list->next) {
4607                         gchar *entry;
4608                         gchar *header;
4609                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4610                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4611                         g_strstrip(entry);
4612                         g_strstrip(header);
4613                         if (strcmp(entry, compose->account->auto_cc)
4614                         ||  strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4615                                 found_other = TRUE;
4616                                 g_free(entry);
4617                                 break;
4618                         }
4619                         g_free(entry);
4620                         g_free(header);
4621                 }
4622                 if (!found_other) {
4623                         AlertValue aval;
4624                         if (compose->batch) {
4625                                 gtk_widget_show_all(compose->window);
4626                         }
4627                         aval = alertpanel(_("Send"),
4628                                           _("The only recipient is the default CC address. Send anyway?"),
4629                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4630                         if (aval != G_ALERTALTERNATE)
4631                                 return FALSE;
4632                 }
4633         }
4634         if (compose->account->set_autobcc && compose->account->auto_bcc) {
4635                 gboolean found_other = FALSE;
4636                 GSList *list;
4637                 /* search header entries for to and newsgroup entries */
4638                 for (list = compose->header_list; list; list = list->next) {
4639                         gchar *entry;
4640                         gchar *header;
4641                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4642                         header = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
4643                         g_strstrip(entry);
4644                         g_strstrip(header);
4645                         if (strcmp(entry, compose->account->auto_bcc)
4646                         ||  strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4647                                 found_other = TRUE;
4648                                 g_free(entry);
4649                                 break;
4650                         }
4651                         g_free(entry);
4652                         g_free(header);
4653                 }
4654                 if (!found_other) {
4655                         AlertValue aval;
4656                         if (compose->batch) {
4657                                 gtk_widget_show_all(compose->window);
4658                         }
4659                         aval = alertpanel(_("Send"),
4660                                           _("The only recipient is the default BCC address. Send anyway?"),
4661                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4662                         if (aval != G_ALERTALTERNATE)
4663                                 return FALSE;
4664                 }
4665         }
4666         return TRUE;
4667 }
4668
4669 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4670 {
4671         const gchar *str;
4672
4673         if (compose_check_for_valid_recipient(compose) == FALSE) {
4674                 if (compose->batch) {
4675                         gtk_widget_show_all(compose->window);
4676                 }
4677                 alertpanel_error(_("Recipient is not specified."));
4678                 return FALSE;
4679         }
4680
4681         if (compose_check_for_set_recipients(compose) == FALSE) {
4682                 return FALSE;
4683         }
4684
4685         if (!compose->batch) {
4686                 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4687                 if (*str == '\0' && check_everything == TRUE && 
4688                     compose->mode != COMPOSE_REDIRECT) {
4689                         AlertValue aval;
4690                         gchar *button_label;
4691                         gchar *message;
4692
4693                         if (compose->sending)
4694                                 button_label = _("+_Send");
4695                         else
4696                                 button_label = _("+_Queue");
4697                         message = g_strdup_printf(_("Subject is empty. %s"),
4698                                         compose->sending?_("Send it anyway?"):
4699                                         _("Queue it anyway?"));
4700
4701                         aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4702                                           GTK_STOCK_CANCEL, button_label, NULL);
4703                         g_free(message);
4704                         if (aval != G_ALERTALTERNATE)
4705                                 return FALSE;
4706                 }
4707         }
4708
4709         if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4710                 return FALSE;
4711
4712         return TRUE;
4713 }
4714
4715 gint compose_send(Compose *compose)
4716 {
4717         gint msgnum;
4718         FolderItem *folder = NULL;
4719         gint val = -1;
4720         gchar *msgpath = NULL;
4721         gboolean discard_window = FALSE;
4722         gchar *errstr = NULL;
4723         gchar *tmsgid = NULL;
4724         MainWindow *mainwin = mainwindow_get_mainwindow();
4725         gboolean queued_removed = FALSE;
4726
4727         if (prefs_common.send_dialog_invisible
4728                         || compose->batch == TRUE)
4729                 discard_window = TRUE;
4730
4731         compose_allow_user_actions (compose, FALSE);
4732         compose->sending = TRUE;
4733
4734         if (compose_check_entries(compose, TRUE) == FALSE) {
4735                 if (compose->batch) {
4736                         gtk_widget_show_all(compose->window);
4737                 }
4738                 goto bail;
4739         }
4740
4741         inc_lock();
4742         val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4743
4744         if (val) {
4745                 if (compose->batch) {
4746                         gtk_widget_show_all(compose->window);
4747                 }
4748                 if (val == -4) {
4749                         alertpanel_error(_("Could not queue message for sending:\n\n"
4750                                            "Charset conversion failed."));
4751                 } else if (val == -5) {
4752                         alertpanel_error(_("Could not queue message for sending:\n\n"
4753                                            "Couldn't get recipient encryption key."));
4754                 } else if (val == -6) {
4755                         /* silent error */
4756                 } else if (val == -3) {
4757                         if (privacy_peek_error())
4758                         alertpanel_error(_("Could not queue message for sending:\n\n"
4759                                            "Signature failed: %s"), privacy_get_error());
4760                 } else if (val == -2 && errno != 0) {
4761                         alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4762                 } else {
4763                         alertpanel_error(_("Could not queue message for sending."));
4764                 }
4765                 goto bail;
4766         }
4767
4768         tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4769         if (discard_window) {
4770                 compose->sending = FALSE;
4771                 compose_close(compose);
4772                 /* No more compose access in the normal codepath 
4773                  * after this point! */
4774                 compose = NULL;
4775         }
4776
4777         if (msgnum == 0) {
4778                 alertpanel_error(_("The message was queued but could not be "
4779                                    "sent.\nUse \"Send queued messages\" from "
4780                                    "the main window to retry."));
4781                 if (!discard_window) {
4782                         goto bail;
4783                 }
4784                 inc_unlock();
4785                 g_free(tmsgid);
4786                 return -1;
4787         }
4788         if (msgpath == NULL) {
4789                 msgpath = folder_item_fetch_msg(folder, msgnum);
4790                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4791                 g_free(msgpath);
4792         } else {
4793                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4794                 claws_unlink(msgpath);
4795                 g_free(msgpath);
4796         }
4797         if (!discard_window) {
4798                 if (val != 0) {
4799                         if (!queued_removed)
4800                                 folder_item_remove_msg(folder, msgnum);
4801                         folder_item_scan(folder);
4802                         if (tmsgid) {
4803                                 /* make sure we delete that */
4804                                 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4805                                 if (tmp) {
4806                                         debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4807                                         folder_item_remove_msg(folder, tmp->msgnum);
4808                                         procmsg_msginfo_free(tmp);
4809                                 } 
4810                         }
4811                 }
4812         }
4813
4814         if (val == 0) {
4815                 if (!queued_removed)
4816                         folder_item_remove_msg(folder, msgnum);
4817                 folder_item_scan(folder);
4818                 if (tmsgid) {
4819                         /* make sure we delete that */
4820                         MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4821                         if (tmp) {
4822                                 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4823                                 folder_item_remove_msg(folder, tmp->msgnum);
4824                                 procmsg_msginfo_free(tmp);
4825                         }
4826                 }
4827                 if (!discard_window) {
4828                         compose->sending = FALSE;
4829                         compose_allow_user_actions (compose, TRUE);
4830                         compose_close(compose);
4831                 }
4832         } else {
4833                 if (errstr) {
4834                         alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4835                                    "the main window to retry."), errstr);
4836                         g_free(errstr);
4837                 } else {
4838                         alertpanel_error_log(_("The message was queued but could not be "
4839                                    "sent.\nUse \"Send queued messages\" from "
4840                                    "the main window to retry."));
4841                 }
4842                 if (!discard_window) {
4843                         goto bail;              
4844                 }
4845                 inc_unlock();
4846                 g_free(tmsgid);
4847                 return -1;
4848         }
4849         g_free(tmsgid);
4850         inc_unlock();
4851         toolbar_main_set_sensitive(mainwin);
4852         main_window_set_menu_sensitive(mainwin);
4853         return 0;
4854
4855 bail:
4856         inc_unlock();
4857         g_free(tmsgid);
4858         compose_allow_user_actions (compose, TRUE);
4859         compose->sending = FALSE;
4860         compose->modified = TRUE; 
4861         toolbar_main_set_sensitive(mainwin);
4862         main_window_set_menu_sensitive(mainwin);
4863
4864         return -1;
4865 }
4866
4867 static gboolean compose_use_attach(Compose *compose) 
4868 {
4869         GtkTreeModel *model = gtk_tree_view_get_model
4870                                 (GTK_TREE_VIEW(compose->attach_clist));
4871         return gtk_tree_model_iter_n_children(model, NULL) > 0;
4872 }
4873
4874 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
4875                                                            FILE *fp)
4876 {
4877         gchar buf[BUFFSIZE];
4878         gchar *str;
4879         gboolean first_to_address;
4880         gboolean first_cc_address;
4881         GSList *list;
4882         ComposeHeaderEntry *headerentry;
4883         const gchar *headerentryname;
4884         const gchar *cc_hdr;
4885         const gchar *to_hdr;
4886         gboolean err = FALSE;
4887
4888         debug_print("Writing redirect header\n");
4889
4890         cc_hdr = prefs_common_translated_header_name("Cc:");
4891         to_hdr = prefs_common_translated_header_name("To:");
4892
4893         first_to_address = TRUE;
4894         for (list = compose->header_list; list; list = list->next) {
4895                 headerentry = ((ComposeHeaderEntry *)list->data);
4896                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4897
4898                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4899                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4900                         Xstrdup_a(str, entstr, return -1);
4901                         g_strstrip(str);
4902                         if (str[0] != '\0') {
4903                                 compose_convert_header
4904                                         (compose, buf, sizeof(buf), str,
4905                                         strlen("Resent-To") + 2, TRUE);
4906
4907                                 if (first_to_address) {
4908                                         err |= (fprintf(fp, "Resent-To: ") < 0);
4909                                         first_to_address = FALSE;
4910                                 } else {
4911                                         err |= (fprintf(fp, ",") < 0);
4912                                 }
4913                                 err |= (fprintf(fp, "%s", buf) < 0);
4914                         }
4915                 }
4916         }
4917         if (!first_to_address) {
4918                 err |= (fprintf(fp, "\n") < 0);
4919         }
4920
4921         first_cc_address = TRUE;
4922         for (list = compose->header_list; list; list = list->next) {
4923                 headerentry = ((ComposeHeaderEntry *)list->data);
4924                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
4925
4926                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4927                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4928                         Xstrdup_a(str, strg, return -1);
4929                         g_strstrip(str);
4930                         if (str[0] != '\0') {
4931                                 compose_convert_header
4932                                         (compose, buf, sizeof(buf), str,
4933                                         strlen("Resent-Cc") + 2, TRUE);
4934
4935                                 if (first_cc_address) {
4936                                         err |= (fprintf(fp, "Resent-Cc: ") < 0);
4937                                         first_cc_address = FALSE;
4938                                 } else {
4939                                         err |= (fprintf(fp, ",") < 0);
4940                                 }
4941                                 err |= (fprintf(fp, "%s", buf) < 0);
4942                         }
4943                 }
4944         }
4945         if (!first_cc_address) {
4946                 err |= (fprintf(fp, "\n") < 0);
4947         }
4948         
4949         return (err ? -1:0);
4950 }
4951
4952 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4953 {
4954         gchar buf[BUFFSIZE];
4955         gchar *str;
4956         const gchar *entstr;
4957         /* struct utsname utsbuf; */
4958         gboolean err = FALSE;
4959
4960         g_return_val_if_fail(fp != NULL, -1);
4961         g_return_val_if_fail(compose->account != NULL, -1);
4962         g_return_val_if_fail(compose->account->address != NULL, -1);
4963
4964         /* Resent-Date */
4965         get_rfc822_date(buf, sizeof(buf));
4966         err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4967
4968         /* Resent-From */
4969         if (compose->account->name && *compose->account->name) {
4970                 compose_convert_header
4971                         (compose, buf, sizeof(buf), compose->account->name,
4972                          strlen("From: "), TRUE);
4973                 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4974                         buf, compose->account->address) < 0);
4975         } else
4976                 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4977
4978         /* Subject */
4979         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4980         if (*entstr != '\0') {
4981                 Xstrdup_a(str, entstr, return -1);
4982                 g_strstrip(str);
4983                 if (*str != '\0') {
4984                         compose_convert_header(compose, buf, sizeof(buf), str,
4985                                                strlen("Subject: "), FALSE);
4986                         err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
4987                 }
4988         }
4989
4990         /* Resent-Message-ID */
4991         if (compose->account->set_domain && compose->account->domain) {
4992                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
4993         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
4994                 g_snprintf(buf, sizeof(buf), "%s", 
4995                         strchr(compose->account->address, '@') ?
4996                                 strchr(compose->account->address, '@')+1 :
4997                                 compose->account->address);
4998         } else {
4999                 g_snprintf(buf, sizeof(buf), "%s", "");
5000         }
5001
5002         if (compose->account->gen_msgid) {
5003                 generate_msgid(buf, sizeof(buf));
5004                 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
5005                 compose->msgid = g_strdup(buf);
5006         } else {
5007                 compose->msgid = NULL;
5008         }
5009
5010         if (compose_redirect_write_headers_from_headerlist(compose, fp))
5011                 return -1;
5012
5013         /* separator between header and body */
5014         err |= (fputs("\n", fp) == EOF);
5015
5016         return (err ? -1:0);
5017 }
5018
5019 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
5020 {
5021         FILE *fp;
5022         size_t len;
5023         gchar buf[BUFFSIZE];
5024         int i = 0;
5025         gboolean skip = FALSE;
5026         gboolean err = FALSE;
5027         gchar *not_included[]={
5028                 "Return-Path:",         "Delivered-To:",        "Received:",
5029                 "Subject:",             "X-UIDL:",              "AF:",
5030                 "NF:",                  "PS:",                  "SRH:",
5031                 "SFN:",                 "DSR:",                 "MID:",
5032                 "CFG:",                 "PT:",                  "S:",
5033                 "RQ:",                  "SSV:",                 "NSV:",
5034                 "SSH:",                 "R:",                   "MAID:",
5035                 "NAID:",                "RMID:",                "FMID:",
5036                 "SCF:",                 "RRCPT:",               "NG:",
5037                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
5038                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
5039                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
5040                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
5041                 "X-Claws-Auto-Wrapping:",
5042                 NULL
5043                 };
5044         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
5045                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
5046                 return -1;
5047         }
5048
5049         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
5050                 skip = FALSE;
5051                 for (i = 0; not_included[i] != NULL; i++) {
5052                         if (g_ascii_strncasecmp(buf, not_included[i],
5053                                                 strlen(not_included[i])) == 0) {
5054                                 skip = TRUE;
5055                                 break;
5056                         }
5057                 }
5058                 if (skip)
5059                         continue;
5060                 if (fputs(buf, fdest) == -1)
5061                         goto error;
5062
5063                 if (!prefs_common.redirect_keep_from) {
5064                         if (g_ascii_strncasecmp(buf, "From:",
5065                                           strlen("From:")) == 0) {
5066                                 err |= (fputs(" (by way of ", fdest) == EOF);
5067                                 if (compose->account->name
5068                                     && *compose->account->name) {
5069                                         compose_convert_header
5070                                                 (compose, buf, sizeof(buf),
5071                                                  compose->account->name,
5072                                                  strlen("From: "),
5073                                                  FALSE);
5074                                         err |= (fprintf(fdest, "%s <%s>",
5075                                                 buf,
5076                                                 compose->account->address) < 0);
5077                                 } else
5078                                         err |= (fprintf(fdest, "%s",
5079                                                 compose->account->address) < 0);
5080                                 err |= (fputs(")", fdest) == EOF);
5081                         }
5082                 }
5083
5084                 if (fputs("\n", fdest) == -1)
5085                         goto error;
5086         }
5087
5088         if (err)
5089                 goto error;
5090
5091         if (compose_redirect_write_headers(compose, fdest))
5092                 goto error;
5093
5094         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
5095                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
5096                         goto error;
5097         }
5098
5099         fclose(fp);
5100
5101         return 0;
5102 error:
5103         fclose(fp);
5104
5105         return -1;
5106 }
5107
5108 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
5109 {
5110         GtkTextBuffer *buffer;
5111         GtkTextIter start, end;
5112         gchar *chars;
5113         gchar *buf;
5114         const gchar *out_codeset;
5115         EncodingType encoding = ENC_UNKNOWN;
5116         MimeInfo *mimemsg, *mimetext;
5117         gint line;
5118         const gchar *src_codeset = CS_INTERNAL;
5119
5120         if (action == COMPOSE_WRITE_FOR_SEND)
5121                 attach_parts = TRUE;
5122
5123         /* create message MimeInfo */
5124         mimemsg = procmime_mimeinfo_new();
5125         mimemsg->type = MIMETYPE_MESSAGE;
5126         mimemsg->subtype = g_strdup("rfc822");
5127         mimemsg->content = MIMECONTENT_MEM;
5128         mimemsg->tmp = TRUE; /* must free content later */
5129         mimemsg->data.mem = compose_get_header(compose);
5130
5131         /* Create text part MimeInfo */
5132         /* get all composed text */
5133         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5134         gtk_text_buffer_get_start_iter(buffer, &start);
5135         gtk_text_buffer_get_end_iter(buffer, &end);
5136         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5137
5138         out_codeset = conv_get_charset_str(compose->out_encoding);
5139
5140         if (!out_codeset && is_ascii_str(chars)) {
5141                 out_codeset = CS_US_ASCII;
5142         } else if (prefs_common.outgoing_fallback_to_ascii &&
5143                    is_ascii_str(chars)) {
5144                 out_codeset = CS_US_ASCII;
5145                 encoding = ENC_7BIT;
5146         }
5147
5148         if (!out_codeset) {
5149                 gchar *test_conv_global_out = NULL;
5150                 gchar *test_conv_reply = NULL;
5151
5152                 /* automatic mode. be automatic. */
5153                 codeconv_set_strict(TRUE);
5154
5155                 out_codeset = conv_get_outgoing_charset_str();
5156                 if (out_codeset) {
5157                         debug_print("trying to convert to %s\n", out_codeset);
5158                         test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5159                 }
5160
5161                 if (!test_conv_global_out && compose->orig_charset
5162                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5163                         out_codeset = compose->orig_charset;
5164                         debug_print("failure; trying to convert to %s\n", out_codeset);
5165                         test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5166                 }
5167
5168                 if (!test_conv_global_out && !test_conv_reply) {
5169                         /* we're lost */
5170                         out_codeset = CS_INTERNAL;
5171                         debug_print("failure; finally using %s\n", out_codeset);
5172                 }
5173                 g_free(test_conv_global_out);
5174                 g_free(test_conv_reply);
5175                 codeconv_set_strict(FALSE);
5176         }
5177
5178         if (encoding == ENC_UNKNOWN) {
5179                 if (prefs_common.encoding_method == CTE_BASE64)
5180                         encoding = ENC_BASE64;
5181                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5182                         encoding = ENC_QUOTED_PRINTABLE;
5183                 else if (prefs_common.encoding_method == CTE_8BIT)
5184                         encoding = ENC_8BIT;
5185                 else
5186                         encoding = procmime_get_encoding_for_charset(out_codeset);
5187         }
5188
5189         debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5190                     src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5191
5192         if (action == COMPOSE_WRITE_FOR_SEND) {
5193                 codeconv_set_strict(TRUE);
5194                 buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5195                 codeconv_set_strict(FALSE);
5196
5197                 if (!buf) {
5198                         AlertValue aval;
5199                         gchar *msg;
5200
5201                         msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5202                                                 "to the specified %s charset.\n"
5203                                                 "Send it as %s?"), out_codeset, src_codeset);
5204                         aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5205                                               NULL, ALERT_ERROR, G_ALERTDEFAULT);
5206                         g_free(msg);
5207
5208                         if (aval != G_ALERTALTERNATE) {
5209                                 g_free(chars);
5210                                 return -3;
5211                         } else {
5212                                 buf = chars;
5213                                 out_codeset = src_codeset;
5214                                 chars = NULL;
5215                         }
5216                 }
5217         } else {
5218                 buf = chars;
5219                 out_codeset = src_codeset;
5220                 chars = NULL;
5221         }
5222         g_free(chars);
5223
5224         if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5225                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5226                     strstr(buf, "\nFrom ") != NULL) {
5227                         encoding = ENC_QUOTED_PRINTABLE;
5228                 }
5229         }
5230
5231         mimetext = procmime_mimeinfo_new();
5232         mimetext->content = MIMECONTENT_MEM;
5233         mimetext->tmp = TRUE; /* must free content later */
5234         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5235          * and free the data, which we need later. */
5236         mimetext->data.mem = g_strdup(buf); 
5237         mimetext->type = MIMETYPE_TEXT;
5238         mimetext->subtype = g_strdup("plain");
5239         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5240                             g_strdup(out_codeset));
5241                             
5242         /* protect trailing spaces when signing message */
5243         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5244             privacy_system_can_sign(compose->privacy_system)) {
5245                 encoding = ENC_QUOTED_PRINTABLE;
5246         }
5247         
5248         debug_print("main text: %zd bytes encoded as %s in %d\n",
5249                 strlen(buf), out_codeset, encoding);
5250
5251         /* check for line length limit */
5252         if (action == COMPOSE_WRITE_FOR_SEND &&
5253             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5254             check_line_length(buf, 1000, &line) < 0) {
5255                 AlertValue aval;
5256                 gchar *msg;
5257
5258                 msg = g_strdup_printf
5259                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5260                            "The contents of the message might be broken on the way to the delivery.\n"
5261                            "\n"
5262                            "Send it anyway?"), line + 1);
5263                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5264                 g_free(msg);
5265                 if (aval != G_ALERTALTERNATE) {
5266                         g_free(buf);
5267                         return -1;
5268                 }
5269         }
5270         
5271         if (encoding != ENC_UNKNOWN)
5272                 procmime_encode_content(mimetext, encoding);
5273
5274         /* append attachment parts */
5275         if (compose_use_attach(compose) && attach_parts) {
5276                 MimeInfo *mimempart;
5277                 gchar *boundary = NULL;
5278                 mimempart = procmime_mimeinfo_new();
5279                 mimempart->content = MIMECONTENT_EMPTY;
5280                 mimempart->type = MIMETYPE_MULTIPART;
5281                 mimempart->subtype = g_strdup("mixed");
5282
5283                 do {
5284                         g_free(boundary);
5285                         boundary = generate_mime_boundary(NULL);
5286                 } while (strstr(buf, boundary) != NULL);
5287
5288                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5289                                     boundary);
5290
5291                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5292
5293                 g_node_append(mimempart->node, mimetext->node);
5294                 g_node_append(mimemsg->node, mimempart->node);
5295
5296                 compose_add_attachments(compose, mimempart);
5297         } else
5298                 g_node_append(mimemsg->node, mimetext->node);
5299
5300         g_free(buf);
5301
5302         /* sign message if sending */
5303         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5304             privacy_system_can_sign(compose->privacy_system))
5305                 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5306                         return -2;
5307
5308         procmime_write_mimeinfo(mimemsg, fp);
5309         
5310         procmime_mimeinfo_free_all(mimemsg);
5311
5312         return 0;
5313 }
5314
5315 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5316 {
5317         GtkTextBuffer *buffer;
5318         GtkTextIter start, end;
5319         FILE *fp;
5320         size_t len;
5321         gchar *chars, *tmp;
5322
5323         if ((fp = g_fopen(file, "wb")) == NULL) {
5324                 FILE_OP_ERROR(file, "fopen");
5325                 return -1;
5326         }
5327
5328         /* chmod for security */
5329         if (change_file_mode_rw(fp, file) < 0) {
5330                 FILE_OP_ERROR(file, "chmod");
5331                 g_warning("can't change file mode\n");
5332         }
5333
5334         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5335         gtk_text_buffer_get_start_iter(buffer, &start);
5336         gtk_text_buffer_get_end_iter(buffer, &end);
5337         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5338
5339         chars = conv_codeset_strdup
5340                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5341
5342         g_free(tmp);
5343         if (!chars) return -1;
5344
5345         /* write body */
5346         len = strlen(chars);
5347         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5348                 FILE_OP_ERROR(file, "fwrite");
5349                 g_free(chars);
5350                 fclose(fp);
5351                 claws_unlink(file);
5352                 return -1;
5353         }
5354
5355         g_free(chars);
5356
5357         if (fclose(fp) == EOF) {
5358                 FILE_OP_ERROR(file, "fclose");
5359                 claws_unlink(file);
5360                 return -1;
5361         }
5362         return 0;
5363 }
5364
5365 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5366 {
5367         FolderItem *item;
5368         MsgInfo *msginfo = compose->targetinfo;
5369
5370         g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5371         if (!msginfo) return -1;
5372
5373         if (!force && MSG_IS_LOCKED(msginfo->flags))
5374                 return 0;
5375
5376         item = msginfo->folder;
5377         g_return_val_if_fail(item != NULL, -1);
5378
5379         if (procmsg_msg_exist(msginfo) &&
5380             (folder_has_parent_of_type(item, F_QUEUE) ||
5381              folder_has_parent_of_type(item, F_DRAFT) 
5382              || msginfo == compose->autosaved_draft)) {
5383                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5384                         g_warning("can't remove the old message\n");
5385                         return -1;
5386                 } else {
5387                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5388                 }
5389         }
5390
5391         return 0;
5392 }
5393
5394 static void compose_remove_draft(Compose *compose)
5395 {
5396         FolderItem *drafts;
5397         MsgInfo *msginfo = compose->targetinfo;
5398         drafts = account_get_special_folder(compose->account, F_DRAFT);
5399
5400         if (procmsg_msg_exist(msginfo)) {
5401                 folder_item_remove_msg(drafts, msginfo->msgnum);
5402         }
5403
5404 }
5405
5406 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5407                    gboolean remove_reedit_target)
5408 {
5409         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5410 }
5411
5412 static gboolean compose_warn_encryption(Compose *compose)
5413 {
5414         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5415         AlertValue val = G_ALERTALTERNATE;
5416         
5417         if (warning == NULL)
5418                 return TRUE;
5419
5420         val = alertpanel_full(_("Encryption warning"), warning,
5421                   GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5422                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5423         if (val & G_ALERTDISABLE) {
5424                 val &= ~G_ALERTDISABLE;
5425                 if (val == G_ALERTALTERNATE)
5426                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5427                                 TRUE);
5428         }
5429
5430         if (val == G_ALERTALTERNATE) {
5431                 return TRUE;
5432         } else {
5433                 return FALSE;
5434         } 
5435 }
5436
5437 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5438                               gchar **msgpath, gboolean check_subject,
5439                               gboolean remove_reedit_target)
5440 {
5441         FolderItem *queue;
5442         gchar *tmp;
5443         FILE *fp;
5444         GSList *cur;
5445         gint num;
5446         static gboolean lock = FALSE;
5447         PrefsAccount *mailac = NULL, *newsac = NULL;
5448         gboolean err = FALSE;
5449
5450         debug_print("queueing message...\n");
5451         g_return_val_if_fail(compose->account != NULL, -1);
5452
5453         lock = TRUE;
5454         
5455         if (compose_check_entries(compose, check_subject) == FALSE) {
5456                 lock = FALSE;
5457                 if (compose->batch) {
5458                         gtk_widget_show_all(compose->window);
5459                 }
5460                 return -1;
5461         }
5462
5463         if (!compose->to_list && !compose->newsgroup_list) {
5464                 g_warning("can't get recipient list.");
5465                 lock = FALSE;
5466                 return -1;
5467         }
5468
5469         if (compose->to_list) {
5470                 if (compose->account->protocol != A_NNTP)
5471                         mailac = compose->account;
5472                 else if (cur_account && cur_account->protocol != A_NNTP)
5473                         mailac = cur_account;
5474                 else if (!(mailac = compose_current_mail_account())) {
5475                         lock = FALSE;
5476                         alertpanel_error(_("No account for sending mails available!"));
5477                         return -1;
5478                 }
5479         }
5480
5481         if (compose->newsgroup_list) {
5482                 if (compose->account->protocol == A_NNTP)
5483                         newsac = compose->account;
5484                 else if (!newsac->protocol != A_NNTP) {
5485                         lock = FALSE;
5486                         alertpanel_error(_("No account for posting news available!"));
5487                         return -1;
5488                 }                       
5489         }
5490
5491         /* write queue header */
5492         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5493                               G_DIR_SEPARATOR, compose, (guint) rand());
5494         debug_print("queuing to %s\n", tmp);
5495         if ((fp = g_fopen(tmp, "wb")) == NULL) {
5496                 FILE_OP_ERROR(tmp, "fopen");
5497                 g_free(tmp);
5498                 lock = FALSE;
5499                 return -2;
5500         }
5501
5502         if (change_file_mode_rw(fp, tmp) < 0) {
5503                 FILE_OP_ERROR(tmp, "chmod");
5504                 g_warning("can't change file mode\n");
5505         }
5506
5507         /* queueing variables */
5508         err |= (fprintf(fp, "AF:\n") < 0);
5509         err |= (fprintf(fp, "NF:0\n") < 0);
5510         err |= (fprintf(fp, "PS:10\n") < 0);
5511         err |= (fprintf(fp, "SRH:1\n") < 0);
5512         err |= (fprintf(fp, "SFN:\n") < 0);
5513         err |= (fprintf(fp, "DSR:\n") < 0);
5514         if (compose->msgid)
5515                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5516         else
5517                 err |= (fprintf(fp, "MID:\n") < 0);
5518         err |= (fprintf(fp, "CFG:\n") < 0);
5519         err |= (fprintf(fp, "PT:0\n") < 0);
5520         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5521         err |= (fprintf(fp, "RQ:\n") < 0);
5522         if (mailac)
5523                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5524         else
5525                 err |= (fprintf(fp, "SSV:\n") < 0);
5526         if (newsac)
5527                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5528         else
5529                 err |= (fprintf(fp, "NSV:\n") < 0);
5530         err |= (fprintf(fp, "SSH:\n") < 0);
5531         /* write recepient list */
5532         if (compose->to_list) {
5533                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5534                 for (cur = compose->to_list->next; cur != NULL;
5535                      cur = cur->next)
5536                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5537                 err |= (fprintf(fp, "\n") < 0);
5538         }
5539         /* write newsgroup list */
5540         if (compose->newsgroup_list) {
5541                 err |= (fprintf(fp, "NG:") < 0);
5542                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5543                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5544                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5545                 err |= (fprintf(fp, "\n") < 0);
5546         }
5547         /* Sylpheed account IDs */
5548         if (mailac)
5549                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5550         if (newsac)
5551                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5552
5553         
5554         if (compose->privacy_system != NULL) {
5555                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5556                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5557                 if (compose->use_encryption) {
5558                         gchar *encdata;
5559                         if (!compose_warn_encryption(compose)) {
5560                                 lock = FALSE;
5561                                 fclose(fp);
5562                                 claws_unlink(tmp);
5563                                 g_free(tmp);
5564                                 return -6;
5565                         }
5566                         if (mailac && mailac->encrypt_to_self) {
5567                                 GSList *tmp_list = g_slist_copy(compose->to_list);
5568                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
5569                                 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5570                                 g_slist_free(tmp_list);
5571                         } else {
5572                                 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5573                         }
5574                         if (encdata != NULL) {
5575                                 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5576                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5577                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
5578                                                 encdata) < 0);
5579                                 } /* else we finally dont want to encrypt */
5580                         } else {
5581                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5582                                 /* and if encdata was null, it means there's been a problem in 
5583                                  * key selection */
5584                                 lock = FALSE;
5585                                 fclose(fp);
5586                                 claws_unlink(tmp);
5587                                 g_free(tmp);
5588                                 return -5;
5589                         }
5590                         g_free(encdata);
5591                 }
5592         }
5593
5594         /* Save copy folder */
5595         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5596                 gchar *savefolderid;
5597                 
5598                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5599                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5600                 g_free(savefolderid);
5601         }
5602         /* Save copy folder */
5603         if (compose->return_receipt) {
5604                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5605         }
5606         /* Message-ID of message replying to */
5607         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5608                 gchar *folderid;
5609                 
5610                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5611                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5612                 g_free(folderid);
5613         }
5614         /* Message-ID of message forwarding to */
5615         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5616                 gchar *folderid;
5617                 
5618                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5619                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5620                 g_free(folderid);
5621         }
5622
5623         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
5624
5625         /* end of headers */
5626         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5627
5628         if (compose->redirect_filename != NULL) {
5629                 if (compose_redirect_write_to_file(compose, fp) < 0) {
5630                         lock = FALSE;
5631                         fclose(fp);
5632                         claws_unlink(tmp);
5633                         g_free(tmp);
5634                         return -2;
5635                 }
5636         } else {
5637                 gint result = 0;
5638                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5639                         lock = FALSE;
5640                         fclose(fp);
5641                         claws_unlink(tmp);
5642                         g_free(tmp);
5643                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5644                 }
5645         }
5646         if (err == TRUE) {
5647                 g_warning("failed to write queue message\n");
5648                 fclose(fp);
5649                 claws_unlink(tmp);
5650                 g_free(tmp);
5651                 lock = FALSE;
5652                 return -2;
5653         }
5654         if (fclose(fp) == EOF) {
5655                 FILE_OP_ERROR(tmp, "fclose");
5656                 claws_unlink(tmp);
5657                 g_free(tmp);
5658                 lock = FALSE;
5659                 return -2;
5660         }
5661
5662         if (item && *item) {
5663                 queue = *item;
5664         } else {
5665                 queue = account_get_special_folder(compose->account, F_QUEUE);
5666         }
5667         if (!queue) {
5668                 g_warning("can't find queue folder\n");
5669                 claws_unlink(tmp);
5670                 g_free(tmp);
5671                 lock = FALSE;
5672                 return -1;
5673         }
5674         folder_item_scan(queue);
5675         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5676                 g_warning("can't queue the message\n");
5677                 claws_unlink(tmp);
5678                 g_free(tmp);
5679                 lock = FALSE;
5680                 return -1;
5681         }
5682         
5683         if (msgpath == NULL) {
5684                 claws_unlink(tmp);
5685                 g_free(tmp);
5686         } else
5687                 *msgpath = tmp;
5688
5689         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5690                 compose_remove_reedit_target(compose, FALSE);
5691         }
5692
5693         if ((msgnum != NULL) && (item != NULL)) {
5694                 *msgnum = num;
5695                 *item = queue;
5696         }
5697
5698         return 0;
5699 }
5700
5701 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5702 {
5703         AttachInfo *ainfo;
5704         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5705         MimeInfo *mimepart;
5706         struct stat statbuf;
5707         gchar *type, *subtype;
5708         GtkTreeModel *model;
5709         GtkTreeIter iter;
5710
5711         model = gtk_tree_view_get_model(tree_view);
5712         
5713         if (!gtk_tree_model_get_iter_first(model, &iter))
5714                 return;
5715         do {
5716                 gtk_tree_model_get(model, &iter,
5717                                    COL_DATA, &ainfo,
5718                                    -1);
5719                                                            
5720                 mimepart = procmime_mimeinfo_new();
5721                 mimepart->content = MIMECONTENT_FILE;
5722                 mimepart->data.filename = g_strdup(ainfo->file);
5723                 mimepart->tmp = FALSE; /* or we destroy our attachment */
5724                 mimepart->offset = 0;
5725
5726                 stat(ainfo->file, &statbuf);
5727                 mimepart->length = statbuf.st_size;
5728
5729                 type = g_strdup(ainfo->content_type);
5730
5731                 if (!strchr(type, '/')) {
5732                         g_free(type);
5733                         type = g_strdup("application/octet-stream");
5734                 }
5735
5736                 subtype = strchr(type, '/') + 1;
5737                 *(subtype - 1) = '\0';
5738                 mimepart->type = procmime_get_media_type(type);
5739                 mimepart->subtype = g_strdup(subtype);
5740                 g_free(type);
5741
5742                 if (mimepart->type == MIMETYPE_MESSAGE && 
5743                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5744                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
5745                 } else {
5746                         if (ainfo->name) {
5747                                 g_hash_table_insert(mimepart->typeparameters,
5748                                             g_strdup("name"), g_strdup(ainfo->name));
5749                                 g_hash_table_insert(mimepart->dispositionparameters,
5750                                             g_strdup("filename"), g_strdup(ainfo->name));
5751                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5752                         }
5753                 }
5754
5755                 if (compose->use_signing) {
5756                         if (ainfo->encoding == ENC_7BIT)
5757                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5758                         else if (ainfo->encoding == ENC_8BIT)
5759                                 ainfo->encoding = ENC_BASE64;
5760                 }
5761                 
5762                 procmime_encode_content(mimepart, ainfo->encoding);
5763
5764                 g_node_append(parent->node, mimepart->node);
5765         } while (gtk_tree_model_iter_next(model, &iter));
5766 }
5767
5768 #define IS_IN_CUSTOM_HEADER(header) \
5769         (compose->account->add_customhdr && \
5770          custom_header_find(compose->account->customhdr_list, header) != NULL)
5771
5772 static void compose_add_headerfield_from_headerlist(Compose *compose, 
5773                                                     GString *header, 
5774                                                     const gchar *fieldname,
5775                                                     const gchar *seperator)
5776 {
5777         gchar *str, *fieldname_w_colon;
5778         gboolean add_field = FALSE;
5779         GSList *list;
5780         ComposeHeaderEntry *headerentry;
5781         const gchar *headerentryname;
5782         const gchar *trans_fieldname;
5783         GString *fieldstr;
5784
5785         if (IS_IN_CUSTOM_HEADER(fieldname))
5786                 return;
5787
5788         debug_print("Adding %s-fields\n", fieldname);
5789
5790         fieldstr = g_string_sized_new(64);
5791
5792         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5793         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5794
5795         for (list = compose->header_list; list; list = list->next) {
5796                 headerentry = ((ComposeHeaderEntry *)list->data);
5797                 headerentryname = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo)))));
5798
5799                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5800                         str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5801                         g_strstrip(str);
5802                         if (str[0] != '\0') {
5803                                 if (add_field)
5804                                         g_string_append(fieldstr, seperator);
5805                                 g_string_append(fieldstr, str);
5806                                 add_field = TRUE;
5807                         }
5808                         g_free(str);
5809                 }
5810         }
5811         if (add_field) {
5812                 gchar *buf;
5813
5814                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5815                 compose_convert_header
5816                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
5817                         strlen(fieldname) + 2, TRUE);
5818                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5819                 g_free(buf);
5820         }
5821
5822         g_free(fieldname_w_colon);
5823         g_string_free(fieldstr, TRUE);
5824
5825         return;
5826 }
5827
5828 static gchar *compose_get_header(Compose *compose)
5829 {
5830         gchar buf[BUFFSIZE];
5831         const gchar *entry_str;
5832         gchar *str;
5833         gchar *name;
5834         GSList *list;
5835         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5836         GString *header;
5837         gchar *from_name = NULL, *from_address = NULL;
5838         gchar *tmp;
5839
5840         g_return_val_if_fail(compose->account != NULL, NULL);
5841         g_return_val_if_fail(compose->account->address != NULL, NULL);
5842
5843         header = g_string_sized_new(64);
5844
5845         /* Date */
5846         get_rfc822_date(buf, sizeof(buf));
5847         g_string_append_printf(header, "Date: %s\n", buf);
5848
5849         /* From */
5850         
5851         if (compose->account->name && *compose->account->name) {
5852                 gchar *buf;
5853                 QUOTE_IF_REQUIRED(buf, compose->account->name);
5854                 tmp = g_strdup_printf("%s <%s>",
5855                         buf, compose->account->address);
5856         } else {
5857                 tmp = g_strdup_printf("%s",
5858                         compose->account->address);
5859         }
5860         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5861         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5862                 /* use default */
5863                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5864                 from_address = g_strdup(compose->account->address);
5865         } else {
5866                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5867                 /* extract name and address */
5868                 if (strstr(spec, " <") && strstr(spec, ">")) {
5869                         from_address = g_strdup(strrchr(spec, '<')+1);
5870                         *(strrchr(from_address, '>')) = '\0';
5871                         from_name = g_strdup(spec);
5872                         *(strrchr(from_name, '<')) = '\0';
5873                 } else {
5874                         from_name = NULL;
5875                         from_address = g_strdup(spec);
5876                 }
5877                 g_free(spec);
5878         }
5879         g_free(tmp);
5880         
5881         
5882         if (from_name && *from_name) {
5883                 compose_convert_header
5884                         (compose, buf, sizeof(buf), from_name,
5885                          strlen("From: "), TRUE);
5886                 QUOTE_IF_REQUIRED(name, buf);
5887                 
5888                 g_string_append_printf(header, "From: %s <%s>\n",
5889                         name, from_address);
5890         } else
5891                 g_string_append_printf(header, "From: %s\n", from_address);
5892         
5893         g_free(from_name);
5894         g_free(from_address);
5895
5896         /* To */
5897         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5898
5899         /* Newsgroups */
5900         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5901
5902         /* Cc */
5903         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5904
5905         /* Bcc */
5906         compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5907
5908         /* Subject */
5909         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5910
5911         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5912                 g_strstrip(str);
5913                 if (*str != '\0') {
5914                         compose_convert_header(compose, buf, sizeof(buf), str,
5915                                                strlen("Subject: "), FALSE);
5916                         g_string_append_printf(header, "Subject: %s\n", buf);
5917                 }
5918         }
5919         g_free(str);
5920
5921         /* Message-ID */
5922         if (compose->account->set_domain && compose->account->domain) {
5923                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
5924         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5925                 g_snprintf(buf, sizeof(buf), "%s", 
5926                         strchr(compose->account->address, '@') ?
5927                                 strchr(compose->account->address, '@')+1 :
5928                                 compose->account->address);
5929         } else {
5930                 g_snprintf(buf, sizeof(buf), "%s", "");
5931         }
5932         
5933         if (compose->account->gen_msgid) {
5934                 generate_msgid(buf, sizeof(buf));
5935                 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5936                 compose->msgid = g_strdup(buf);
5937         } else {
5938                 compose->msgid = NULL;
5939         }
5940
5941         if (compose->remove_references == FALSE) {
5942                 /* In-Reply-To */
5943                 if (compose->inreplyto && compose->to_list)
5944                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5945         
5946                 /* References */
5947                 if (compose->references)
5948                         g_string_append_printf(header, "References: %s\n", compose->references);
5949         }
5950
5951         /* Followup-To */
5952         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5953
5954         /* Reply-To */
5955         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5956
5957         /* Organization */
5958         if (compose->account->organization &&
5959             strlen(compose->account->organization) &&
5960             !IS_IN_CUSTOM_HEADER("Organization")) {
5961                 compose_convert_header(compose, buf, sizeof(buf),
5962                                        compose->account->organization,
5963                                        strlen("Organization: "), FALSE);
5964                 g_string_append_printf(header, "Organization: %s\n", buf);
5965         }
5966
5967         /* Program version and system info */
5968         if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5969             !compose->newsgroup_list) {
5970                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5971                         prog_version,
5972                         gtk_major_version, gtk_minor_version, gtk_micro_version,
5973                         TARGET_ALIAS);
5974         }
5975         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5976                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5977                         prog_version,
5978                         gtk_major_version, gtk_minor_version, gtk_micro_version,
5979                         TARGET_ALIAS);
5980         }
5981
5982         /* custom headers */
5983         if (compose->account->add_customhdr) {
5984                 GSList *cur;
5985
5986                 for (cur = compose->account->customhdr_list; cur != NULL;
5987                      cur = cur->next) {
5988                         CustomHeader *chdr = (CustomHeader *)cur->data;
5989
5990                         if (custom_header_is_allowed(chdr->name)) {
5991                                 compose_convert_header
5992                                         (compose, buf, sizeof(buf),
5993                                          chdr->value ? chdr->value : "",
5994                                          strlen(chdr->name) + 2, FALSE);
5995                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
5996                         }
5997                 }
5998         }
5999
6000         /* Automatic Faces and X-Faces */
6001         if (get_account_xface (buf, sizeof(buf), compose->account->account_name) == 0) {
6002                 g_string_append_printf(header, "X-Face: %s\n", buf);
6003         }
6004         else if (get_default_xface (buf, sizeof(buf)) == 0) {
6005                 g_string_append_printf(header, "X-Face: %s\n", buf);
6006         }
6007         if (get_account_face (buf, sizeof(buf), compose->account->account_name) == 0) {
6008                 g_string_append_printf(header, "Face: %s\n", buf);
6009         }
6010         else if (get_default_face (buf, sizeof(buf)) == 0) {
6011                 g_string_append_printf(header, "Face: %s\n", buf);
6012         }
6013
6014         /* PRIORITY */
6015         switch (compose->priority) {
6016                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
6017                                                    "X-Priority: 1 (Highest)\n");
6018                         break;
6019                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
6020                                                 "X-Priority: 2 (High)\n");
6021                         break;
6022                 case PRIORITY_NORMAL: break;
6023                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
6024                                                "X-Priority: 4 (Low)\n");
6025                         break;
6026                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
6027                                                   "X-Priority: 5 (Lowest)\n");
6028                         break;
6029                 default: debug_print("compose: priority unknown : %d\n",
6030                                      compose->priority);
6031         }
6032
6033         /* Request Return Receipt */
6034         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
6035                 if (compose->return_receipt) {
6036                         if (compose->account->name
6037                             && *compose->account->name) {
6038                                 compose_convert_header(compose, buf, sizeof(buf), 
6039                                                        compose->account->name, 
6040                                                        strlen("Disposition-Notification-To: "),
6041                                                        TRUE);
6042                                 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
6043                         } else
6044                                 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
6045                 }
6046         }
6047
6048         /* get special headers */
6049         for (list = compose->header_list; list; list = list->next) {
6050                 ComposeHeaderEntry *headerentry;
6051                 gchar *tmp;
6052                 gchar *headername;
6053                 gchar *headername_wcolon;
6054                 const gchar *headername_trans;
6055                 gchar *headervalue;
6056                 gchar **string;
6057                 gboolean standard_header = FALSE;
6058
6059                 headerentry = ((ComposeHeaderEntry *)list->data);
6060
6061                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((headerentry->combo))))));
6062                 g_strstrip(tmp);
6063                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
6064                         g_free(tmp);
6065                         continue;
6066                 }
6067
6068                 if (!strstr(tmp, ":")) {
6069                         headername_wcolon = g_strconcat(tmp, ":", NULL);
6070                         headername = g_strdup(tmp);
6071                 } else {
6072                         headername_wcolon = g_strdup(tmp);
6073                         headername = g_strdup(strtok(tmp, ":"));
6074                 }
6075                 g_free(tmp);
6076                 
6077                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
6078                 Xstrdup_a(headervalue, entry_str, return NULL);
6079                 subst_char(headervalue, '\r', ' ');
6080                 subst_char(headervalue, '\n', ' ');
6081                 string = std_headers;
6082                 while (*string != NULL) {
6083                         headername_trans = prefs_common_translated_header_name(*string);
6084                         if (!strcmp(headername_trans, headername_wcolon))
6085                                 standard_header = TRUE;
6086                         string++;
6087                 }
6088                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
6089                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
6090                                 
6091                 g_free(headername);
6092                 g_free(headername_wcolon);              
6093         }
6094
6095         str = header->str;
6096         g_string_free(header, FALSE);
6097
6098         return str;
6099 }
6100
6101 #undef IS_IN_CUSTOM_HEADER
6102
6103 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
6104                                    gint header_len, gboolean addr_field)
6105 {
6106         gchar *tmpstr = NULL;
6107         const gchar *out_codeset = NULL;
6108
6109         g_return_if_fail(src != NULL);
6110         g_return_if_fail(dest != NULL);
6111
6112         if (len < 1) return;
6113
6114         tmpstr = g_strdup(src);
6115
6116         subst_char(tmpstr, '\n', ' ');
6117         subst_char(tmpstr, '\r', ' ');
6118         g_strchomp(tmpstr);
6119
6120         if (!g_utf8_validate(tmpstr, -1, NULL)) {
6121                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
6122                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
6123                 g_free(tmpstr);
6124                 tmpstr = mybuf;
6125         }
6126
6127         codeconv_set_strict(TRUE);
6128         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6129                 conv_get_charset_str(compose->out_encoding));
6130         codeconv_set_strict(FALSE);
6131         
6132         if (!dest || *dest == '\0') {
6133                 gchar *test_conv_global_out = NULL;
6134                 gchar *test_conv_reply = NULL;
6135
6136                 /* automatic mode. be automatic. */
6137                 codeconv_set_strict(TRUE);
6138
6139                 out_codeset = conv_get_outgoing_charset_str();
6140                 if (out_codeset) {
6141                         debug_print("trying to convert to %s\n", out_codeset);
6142                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6143                 }
6144
6145                 if (!test_conv_global_out && compose->orig_charset
6146                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
6147                         out_codeset = compose->orig_charset;
6148                         debug_print("failure; trying to convert to %s\n", out_codeset);
6149                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
6150                 }
6151
6152                 if (!test_conv_global_out && !test_conv_reply) {
6153                         /* we're lost */
6154                         out_codeset = CS_INTERNAL;
6155                         debug_print("finally using %s\n", out_codeset);
6156                 }
6157                 g_free(test_conv_global_out);
6158                 g_free(test_conv_reply);
6159                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6160                                         out_codeset);
6161                 codeconv_set_strict(FALSE);
6162         }
6163         g_free(tmpstr);
6164 }
6165
6166 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6167 {
6168         gchar *address;
6169
6170         g_return_if_fail(user_data != NULL);
6171
6172         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6173         g_strstrip(address);
6174         if (*address != '\0') {
6175                 gchar *name = procheader_get_fromname(address);
6176                 extract_address(address);
6177                 addressbook_add_contact(name, address, NULL, NULL);
6178         }
6179         g_free(address);
6180 }
6181
6182 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6183 {
6184         GtkWidget *menuitem;
6185         gchar *address;
6186
6187         g_return_if_fail(menu != NULL);
6188         g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6189
6190         menuitem = gtk_separator_menu_item_new();
6191         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6192         gtk_widget_show(menuitem);
6193
6194         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6195         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6196
6197         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6198         g_strstrip(address);
6199         if (*address == '\0') {
6200                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6201         }
6202
6203         g_signal_connect(G_OBJECT(menuitem), "activate",
6204                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
6205         gtk_widget_show(menuitem);
6206 }
6207
6208 static void compose_create_header_entry(Compose *compose) 
6209 {
6210         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6211
6212         GtkWidget *combo;
6213         GtkWidget *entry;
6214         gchar **string;
6215         const gchar *header = NULL;
6216         ComposeHeaderEntry *headerentry;
6217         gboolean standard_header = FALSE;
6218 #if !(GTK_CHECK_VERSION(2,12,0))
6219         GtkTooltips *tips = compose->tooltips;
6220 #endif
6221         
6222         headerentry = g_new0(ComposeHeaderEntry, 1);
6223
6224         /* Combo box */
6225         combo = gtk_combo_box_entry_new_text();
6226         string = headers; 
6227         while(*string != NULL) {
6228                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6229                         (gchar*)prefs_common_translated_header_name(*string));
6230                 string++;
6231         }
6232         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6233         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6234                          G_CALLBACK(compose_grab_focus_cb), compose);
6235         gtk_widget_show(combo);
6236         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6237                         compose->header_nextrow, compose->header_nextrow+1,
6238                         GTK_SHRINK, GTK_FILL, 0, 0);
6239         if (compose->header_last) {     
6240                 const gchar *last_header_entry = gtk_entry_get_text(
6241                                 GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6242                 string = headers;
6243                 while (*string != NULL) {
6244                         if (!strcmp(*string, last_header_entry))
6245                                 standard_header = TRUE;
6246                         string++;
6247                 }
6248                 if (standard_header)
6249                         header = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))));
6250         }
6251         if (!compose->header_last || !standard_header) {
6252                 switch(compose->account->protocol) {
6253                         case A_NNTP:
6254                                 header = prefs_common_translated_header_name("Newsgroups:");
6255                                 break;
6256                         default:
6257                                 header = prefs_common_translated_header_name("To:");
6258                                 break;
6259                 }                                                                   
6260         }
6261         if (header)
6262                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((combo)))), header);
6263
6264         g_signal_connect_after(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "grab_focus",
6265                          G_CALLBACK(compose_grab_focus_cb), compose);
6266
6267         /* Entry field */
6268         entry = gtk_entry_new(); 
6269         gtk_widget_show(entry);
6270         CLAWS_SET_TIP(entry,
6271                 _("Use <tab> to autocomplete from addressbook"));
6272         gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6273                         compose->header_nextrow, compose->header_nextrow+1,
6274                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6275
6276         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6277                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6278                          headerentry);
6279         g_signal_connect(G_OBJECT(entry), "changed", 
6280                          G_CALLBACK(compose_headerentry_changed_cb), 
6281                          headerentry);
6282         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6283                          G_CALLBACK(compose_grab_focus_cb), compose);
6284                          
6285         /* email dnd */
6286         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6287                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6288                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6289         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6290                          G_CALLBACK(compose_header_drag_received_cb),
6291                          entry);
6292         g_signal_connect(G_OBJECT(entry), "drag-drop",
6293                          G_CALLBACK(compose_drag_drop),
6294                          compose);
6295         g_signal_connect(G_OBJECT(entry), "populate-popup",
6296                          G_CALLBACK(compose_entry_popup_extend),
6297                          NULL);
6298         
6299         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6300
6301         headerentry->compose = compose;
6302         headerentry->combo = combo;
6303         headerentry->entry = entry;
6304         headerentry->headernum = compose->header_nextrow;
6305
6306         compose->header_nextrow++;
6307         compose->header_last = headerentry;             
6308         compose->header_list =
6309                 g_slist_append(compose->header_list,
6310                                headerentry);
6311 }
6312
6313 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text) 
6314 {
6315         ComposeHeaderEntry *last_header;
6316         
6317         last_header = compose->header_last;
6318
6319         gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((last_header->combo)))), header);
6320         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6321 }
6322
6323 static void compose_remove_header_entries(Compose *compose) 
6324 {
6325         GSList *list;
6326         for (list = compose->header_list; list; list = list->next) {
6327                 ComposeHeaderEntry *headerentry = 
6328                         (ComposeHeaderEntry *)list->data;
6329                 gtk_widget_destroy(headerentry->combo);
6330                 gtk_widget_destroy(headerentry->entry);
6331                 g_free(headerentry);
6332         }
6333         compose->header_last = NULL;
6334         g_slist_free(compose->header_list);
6335         compose->header_list = NULL;
6336         compose->header_nextrow = 1;
6337         compose_create_header_entry(compose);
6338 }
6339
6340 static GtkWidget *compose_create_header(Compose *compose) 
6341 {
6342         GtkWidget *from_optmenu_hbox;
6343         GtkWidget *header_scrolledwin;
6344         GtkWidget *header_table;
6345
6346         gint count = 0;
6347
6348         /* header labels and entries */
6349         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6350         gtk_widget_show(header_scrolledwin);
6351         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6352
6353         header_table = gtk_table_new(2, 2, FALSE);
6354         gtk_widget_show(header_table);
6355         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6356         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6357         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN((header_scrolledwin)))), GTK_SHADOW_NONE);
6358         count = 0;
6359
6360         /* option menu for selecting accounts */
6361         from_optmenu_hbox = compose_account_option_menu_create(compose);
6362         gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6363                                   0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6364         count++;
6365
6366         compose->header_table = header_table;
6367         compose->header_list = NULL;
6368         compose->header_nextrow = count;
6369
6370         compose_create_header_entry(compose);
6371
6372         compose->table            = NULL;
6373
6374         return header_scrolledwin ;
6375 }
6376
6377 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6378 {
6379         Compose *compose = (Compose *)data;
6380         GdkEventButton event;
6381         
6382         event.button = 3;
6383         event.time = gtk_get_current_event_time();
6384
6385         return attach_button_pressed(compose->attach_clist, &event, compose);
6386 }
6387
6388 static GtkWidget *compose_create_attach(Compose *compose)
6389 {
6390         GtkWidget *attach_scrwin;
6391         GtkWidget *attach_clist;
6392
6393         GtkListStore *store;
6394         GtkCellRenderer *renderer;
6395         GtkTreeViewColumn *column;
6396         GtkTreeSelection *selection;
6397
6398         /* attachment list */
6399         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6400         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6401                                        GTK_POLICY_AUTOMATIC,
6402                                        GTK_POLICY_AUTOMATIC);
6403         gtk_widget_set_size_request(attach_scrwin, -1, 80);
6404
6405         store = gtk_list_store_new(N_ATTACH_COLS, 
6406                                    G_TYPE_STRING,
6407                                    G_TYPE_STRING,
6408                                    G_TYPE_STRING,
6409                                    G_TYPE_POINTER,
6410                                    G_TYPE_AUTO_POINTER,
6411                                    -1);
6412         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6413                                         (GTK_TREE_MODEL(store)));
6414         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6415         g_object_unref(store);
6416         
6417         renderer = gtk_cell_renderer_text_new();
6418         column = gtk_tree_view_column_new_with_attributes
6419                         (_("Mime type"), renderer, "text", 
6420                          COL_MIMETYPE, NULL);
6421         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6422         
6423         renderer = gtk_cell_renderer_text_new();
6424         column = gtk_tree_view_column_new_with_attributes
6425                         (_("Size"), renderer, "text", 
6426                          COL_SIZE, NULL);
6427         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6428         
6429         renderer = gtk_cell_renderer_text_new();
6430         column = gtk_tree_view_column_new_with_attributes
6431                         (_("Name"), renderer, "text", 
6432                          COL_NAME, NULL);
6433         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6434
6435         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6436                                      prefs_common.use_stripes_everywhere);
6437         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6438         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6439
6440         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6441                          G_CALLBACK(attach_selected), compose);
6442         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6443                          G_CALLBACK(attach_button_pressed), compose);
6444 #ifndef MAEMO
6445         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6446                          G_CALLBACK(popup_attach_button_pressed), compose);
6447 #else
6448         gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6449                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6450         g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6451                          G_CALLBACK(popup_attach_button_pressed), compose);
6452 #endif
6453         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6454                          G_CALLBACK(attach_key_pressed), compose);
6455
6456         /* drag and drop */
6457         gtk_drag_dest_set(attach_clist,
6458                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6459                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6460                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6461         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6462                          G_CALLBACK(compose_attach_drag_received_cb),
6463                          compose);
6464         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6465                          G_CALLBACK(compose_drag_drop),
6466                          compose);
6467
6468         compose->attach_scrwin = attach_scrwin;
6469         compose->attach_clist  = attach_clist;
6470
6471         return attach_scrwin;
6472 }
6473
6474 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6475 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6476
6477 static GtkWidget *compose_create_others(Compose *compose)
6478 {
6479         GtkWidget *table;
6480         GtkWidget *savemsg_checkbtn;
6481         GtkWidget *savemsg_entry;
6482         GtkWidget *savemsg_select;
6483         
6484         guint rowcount = 0;
6485         gchar *folderidentifier;
6486
6487         /* Table for settings */
6488         table = gtk_table_new(3, 1, FALSE);
6489         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6490         gtk_widget_show(table);
6491         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6492         rowcount = 0;
6493
6494         /* Save Message to folder */
6495         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6496         gtk_widget_show(savemsg_checkbtn);
6497         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6498         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6499                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6500         }
6501         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6502                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6503
6504         savemsg_entry = gtk_entry_new();
6505         gtk_widget_show(savemsg_entry);
6506         gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6507         gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6508         g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6509                          G_CALLBACK(compose_grab_focus_cb), compose);
6510         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6511                 folderidentifier = folder_item_get_identifier(account_get_special_folder
6512                                   (compose->account, F_OUTBOX));
6513                 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6514                 g_free(folderidentifier);
6515         }
6516
6517         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6518         gtk_widget_show(savemsg_select);
6519         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6520         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6521                          G_CALLBACK(compose_savemsg_select_cb),
6522                          compose);
6523
6524         rowcount++;
6525
6526         compose->savemsg_checkbtn = savemsg_checkbtn;
6527         compose->savemsg_entry = savemsg_entry;
6528
6529         return table;   
6530 }
6531
6532 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
6533 {
6534         gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6535                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6536 }
6537
6538 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6539 {
6540         FolderItem *dest;
6541         gchar * path;
6542
6543         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE);
6544         if (!dest) return;
6545
6546         path = folder_item_get_identifier(dest);
6547
6548         gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6549         g_free(path);
6550 }
6551
6552 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6553                                   GdkAtom clip, GtkTextIter *insert_place);
6554
6555
6556 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6557                                        Compose *compose)
6558 {
6559         gint prev_autowrap;
6560         GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6561 #if USE_ASPELL
6562         if (event->button == 3) {
6563                 GtkTextIter iter;
6564                 GtkTextIter sel_start, sel_end;
6565                 gboolean stuff_selected;
6566                 gint x, y;
6567                 /* move the cursor to allow GtkAspell to check the word
6568                  * under the mouse */
6569                 if (event->x && event->y) {
6570                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6571                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6572                                 &x, &y);
6573                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6574                                 &iter, x, y);
6575                 } else {
6576                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6577                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6578                 }
6579                 /* get selection */
6580                 stuff_selected = gtk_text_buffer_get_selection_bounds(
6581                                 buffer,
6582                                 &sel_start, &sel_end);
6583
6584                 gtk_text_buffer_place_cursor (buffer, &iter);
6585                 /* reselect stuff */
6586                 if (stuff_selected 
6587                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6588                         gtk_text_buffer_select_range(buffer,
6589                                 &sel_start, &sel_end);
6590                 }
6591                 return FALSE; /* pass the event so that the right-click goes through */
6592         }
6593 #endif
6594         if (event->button == 2) {
6595                 GtkTextIter iter;
6596                 gint x, y;
6597                 BLOCK_WRAP();
6598                 
6599                 /* get the middle-click position to paste at the correct place */
6600                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6601                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6602                         &x, &y);
6603                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6604                         &iter, x, y);
6605                 
6606                 entry_paste_clipboard(compose, text, 
6607                                 prefs_common.linewrap_pastes,
6608                                 GDK_SELECTION_PRIMARY, &iter);
6609                 UNBLOCK_WRAP();
6610                 return TRUE;
6611         }
6612         return FALSE;
6613 }
6614
6615 #if USE_ASPELL
6616 static void compose_spell_menu_changed(void *data)
6617 {
6618         Compose *compose = (Compose *)data;
6619         GSList *items;
6620         GtkWidget *menuitem;
6621         GtkWidget *parent_item;
6622         GtkMenu *menu = GTK_MENU(gtk_menu_new());
6623         GSList *spell_menu;
6624
6625         if (compose->gtkaspell == NULL)
6626                 return;
6627
6628         parent_item = gtk_ui_manager_get_widget(compose->ui_manager, 
6629                         "/Menu/Spelling/Options");
6630
6631         /* setting the submenu removes /Spelling/Options from the factory 
6632          * so we need to save it */
6633
6634         if (parent_item == NULL) {
6635                 parent_item = compose->aspell_options_menu;
6636                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), NULL);
6637         } else
6638                 compose->aspell_options_menu = parent_item;
6639
6640         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6641
6642         spell_menu = g_slist_reverse(spell_menu);
6643         for (items = spell_menu;
6644              items; items = items->next) {
6645                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6646                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6647                 gtk_widget_show(GTK_WIDGET(menuitem));
6648         }
6649         g_slist_free(spell_menu);
6650
6651         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6652         
6653 }
6654 #endif
6655
6656 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6657 {
6658         Compose *compose = (Compose *)data;
6659         GdkEventButton event;
6660         
6661         event.button = 3;
6662         event.time = gtk_get_current_event_time();
6663         event.x = 0;
6664         event.y = 0;
6665
6666         return text_clicked(compose->text, &event, compose);
6667 }
6668
6669 static gboolean compose_force_window_origin = TRUE;
6670 static Compose *compose_create(PrefsAccount *account,
6671                                                  FolderItem *folder,
6672                                                  ComposeMode mode,
6673                                                  gboolean batch)
6674 {
6675         Compose   *compose;
6676         GtkWidget *window;
6677         GtkWidget *vbox;
6678         GtkWidget *menubar;
6679         GtkWidget *handlebox;
6680
6681         GtkWidget *notebook;
6682         
6683         GtkWidget *attach_hbox;
6684         GtkWidget *attach_lab1;
6685         GtkWidget *attach_lab2;
6686
6687         GtkWidget *vbox2;
6688
6689         GtkWidget *label;
6690         GtkWidget *subject_hbox;
6691         GtkWidget *subject_frame;
6692         GtkWidget *subject_entry;
6693         GtkWidget *subject;
6694         GtkWidget *paned;
6695
6696         GtkWidget *edit_vbox;
6697         GtkWidget *ruler_hbox;
6698         GtkWidget *ruler;
6699         GtkWidget *scrolledwin;
6700         GtkWidget *text;
6701         GtkTextBuffer *buffer;
6702         GtkClipboard *clipboard;
6703         CLAWS_TIP_DECL();
6704
6705         UndoMain *undostruct;
6706
6707         gchar *titles[N_ATTACH_COLS];
6708         GtkWidget *popupmenu;
6709         GtkWidget *tmpl_menu;
6710         GtkActionGroup *action_group = NULL;
6711
6712 #if USE_ASPELL
6713         GtkAspell * gtkaspell = NULL;
6714 #endif
6715
6716         static GdkGeometry geometry;
6717
6718         g_return_val_if_fail(account != NULL, NULL);
6719
6720         debug_print("Creating compose window...\n");
6721         compose = g_new0(Compose, 1);
6722
6723         titles[COL_MIMETYPE] = _("MIME type");
6724         titles[COL_SIZE]     = _("Size");
6725         titles[COL_NAME]     = _("Name");
6726
6727         compose->batch = batch;
6728         compose->account = account;
6729         compose->folder = folder;
6730         
6731         compose->mutex = g_mutex_new();
6732         compose->set_cursor_pos = -1;
6733
6734 #if !(GTK_CHECK_VERSION(2,12,0))
6735         compose->tooltips = tips;
6736 #endif
6737
6738         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6739
6740         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6741         gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6742
6743         if (!geometry.max_width) {
6744                 geometry.max_width = gdk_screen_width();
6745                 geometry.max_height = gdk_screen_height();
6746         }
6747
6748         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6749                                       &geometry, GDK_HINT_MAX_SIZE);
6750         if (!geometry.min_width) {
6751                 geometry.min_width = 600;
6752                 geometry.min_height = 480;
6753         }
6754         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6755                                       &geometry, GDK_HINT_MIN_SIZE);
6756
6757 #ifndef GENERIC_UMPC    
6758         if (compose_force_window_origin)
6759                 gtk_window_move(GTK_WINDOW(window), prefs_common.compose_x, 
6760                                  prefs_common.compose_y);
6761 #endif
6762         g_signal_connect(G_OBJECT(window), "delete_event",
6763                          G_CALLBACK(compose_delete_cb), compose);
6764         MANAGE_WINDOW_SIGNALS_CONNECT(window);
6765         gtk_widget_realize(window);
6766
6767         gtkut_widget_set_composer_icon(window);
6768
6769         vbox = gtk_vbox_new(FALSE, 0);
6770         gtk_container_add(GTK_CONTAINER(window), vbox);
6771
6772         compose->ui_manager = gtk_ui_manager_new();
6773         action_group = cm_menu_create_action_group_full(compose->ui_manager,"Menu", compose_entries,
6774                         G_N_ELEMENTS(compose_entries), (gpointer)compose);
6775         gtk_action_group_add_toggle_actions(action_group, compose_toggle_entries,
6776                         G_N_ELEMENTS(compose_toggle_entries), (gpointer)compose);
6777         gtk_action_group_add_radio_actions(action_group, compose_radio_rm_entries,
6778                         G_N_ELEMENTS(compose_radio_rm_entries), COMPOSE_REPLY, G_CALLBACK(compose_reply_change_mode_cb), (gpointer)compose);
6779         gtk_action_group_add_radio_actions(action_group, compose_radio_prio_entries,
6780                         G_N_ELEMENTS(compose_radio_prio_entries), PRIORITY_NORMAL, G_CALLBACK(compose_set_priority_cb), (gpointer)compose);
6781         gtk_action_group_add_radio_actions(action_group, compose_radio_enc_entries,
6782                         G_N_ELEMENTS(compose_radio_enc_entries), C_AUTO, G_CALLBACK(compose_set_encoding_cb), (gpointer)compose);
6783
6784 #ifndef MAEMO
6785         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
6786 #else
6787         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENU)
6788 #endif
6789
6790         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Message", "Message", GTK_UI_MANAGER_MENU)
6791         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
6792 #ifdef USE_ASPELL
6793         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Spelling", "Spelling", GTK_UI_MANAGER_MENU)
6794 #endif
6795         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Options", "Options", GTK_UI_MANAGER_MENU)
6796         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Tools", "Tools", GTK_UI_MANAGER_MENU)
6797         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu", "Help", "Help", GTK_UI_MANAGER_MENU)
6798
6799 /* Compose menu */
6800         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Send", "Message/Send", GTK_UI_MANAGER_MENUITEM)
6801         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "SendLater", "Message/SendLater", GTK_UI_MANAGER_MENUITEM)
6802         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6803         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "AttachFile", "Message/AttachFile", GTK_UI_MANAGER_MENUITEM)
6804         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertFile", "Message/InsertFile", GTK_UI_MANAGER_MENUITEM)
6805         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "InsertSig", "Message/InsertSig", GTK_UI_MANAGER_MENUITEM)
6806         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6807         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Save", "Message/Save", GTK_UI_MANAGER_MENUITEM)
6808         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
6809         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Message", "Close", "Message/Close", GTK_UI_MANAGER_MENUITEM)
6810
6811 /* Edit menu */
6812         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
6813         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
6814         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6815
6816         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
6817         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
6818         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
6819
6820         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SpecialPaste", "Edit/SpecialPaste", GTK_UI_MANAGER_MENU)
6821         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "AsQuotation", "Edit/SpecialPaste/AsQuotation", GTK_UI_MANAGER_MENUITEM)
6822         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Wrapped", "Edit/SpecialPaste/Wrapped", GTK_UI_MANAGER_MENUITEM)
6823         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/SpecialPaste", "Unwrapped", "Edit/SpecialPaste/Unwrapped", GTK_UI_MANAGER_MENUITEM)
6824
6825         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
6826
6827         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Advanced", "Edit/Advanced", GTK_UI_MANAGER_MENU)
6828         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackChar", "Edit/Advanced/BackChar", GTK_UI_MANAGER_MENUITEM)
6829         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwChar", "Edit/Advanced/ForwChar", GTK_UI_MANAGER_MENUITEM)
6830         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BackWord", "Edit/Advanced/BackWord", GTK_UI_MANAGER_MENUITEM)
6831         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "ForwWord", "Edit/Advanced/ForwWord", GTK_UI_MANAGER_MENUITEM)
6832         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "BegLine", "Edit/Advanced/BegLine", GTK_UI_MANAGER_MENUITEM)
6833         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "EndLine", "Edit/Advanced/EndLine", GTK_UI_MANAGER_MENUITEM)
6834         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "PrevLine", "Edit/Advanced/PrevLine", GTK_UI_MANAGER_MENUITEM)
6835         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "NextLine", "Edit/Advanced/NextLine", GTK_UI_MANAGER_MENUITEM)
6836         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackChar", "Edit/Advanced/DelBackChar", GTK_UI_MANAGER_MENUITEM)
6837         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwChar", "Edit/Advanced/DelForwChar", GTK_UI_MANAGER_MENUITEM)
6838         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelBackWord", "Edit/Advanced/DelBackWord", GTK_UI_MANAGER_MENUITEM)
6839         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelForwWord", "Edit/Advanced/DelForwWord", GTK_UI_MANAGER_MENUITEM)
6840         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelLine", "Edit/Advanced/DelLine", GTK_UI_MANAGER_MENUITEM)
6841         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit/Advanced", "DelEndLine", "Edit/Advanced/DelEndLine", GTK_UI_MANAGER_MENUITEM)
6842
6843         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6844
6845         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
6846         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapPara", "Edit/WrapPara", GTK_UI_MANAGER_MENUITEM)
6847         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "WrapAllLines", "Edit/WrapAllLines", GTK_UI_MANAGER_MENUITEM)
6848         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "AutoWrap", "Edit/AutoWrap", GTK_UI_MANAGER_MENUITEM)
6849
6850         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "Separator3", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
6851
6852         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Edit", "ExtEditor", "Edit/ExtEditor", GTK_UI_MANAGER_MENUITEM)
6853
6854 #if USE_ASPELL
6855 /* Spelling menu */
6856         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckAllSel", "Spelling/CheckAllSel", GTK_UI_MANAGER_MENUITEM)
6857         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "HighlightAll", "Spelling/HighlightAll", GTK_UI_MANAGER_MENUITEM)
6858         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "CheckBackwards", "Spelling/CheckBackwards", GTK_UI_MANAGER_MENUITEM)
6859         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "ForwardNext", "Spelling/ForwardNext", GTK_UI_MANAGER_MENUITEM)
6860         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Separator1", "Spelling/---", GTK_UI_MANAGER_SEPARATOR)
6861         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Spelling", "Options", "Spelling/Options", GTK_UI_MANAGER_MENU)
6862 #endif
6863
6864 /* Options menu */
6865         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "ReplyMode", "Options/ReplyMode", GTK_UI_MANAGER_MENU)
6866         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Normal", "Options/ReplyMode/Normal", GTK_UI_MANAGER_MENUITEM)
6867         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "All", "Options/ReplyMode/All", GTK_UI_MANAGER_MENUITEM)
6868         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "Sender", "Options/ReplyMode/Sender", GTK_UI_MANAGER_MENUITEM)
6869         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/ReplyMode", "List", "Options/ReplyMode/List", GTK_UI_MANAGER_MENUITEM)
6870
6871         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator1", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6872         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "PrivacySystem", "Options/PrivacySystem", GTK_UI_MANAGER_MENU)
6873         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/PrivacySystem", "PlaceHolder", "Options/PrivacySystem/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6874         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Sign", "Options/Sign", GTK_UI_MANAGER_MENUITEM)
6875         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encrypt", "Options/Encrypt", GTK_UI_MANAGER_MENUITEM)
6876
6877         
6878         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator2", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6879         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Priority", "Options/Priority", GTK_UI_MANAGER_MENU)
6880         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Highest", "Options/Priority/Highest", GTK_UI_MANAGER_MENUITEM)
6881         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "High", "Options/Priority/High", GTK_UI_MANAGER_MENUITEM)
6882         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Normal", "Options/Priority/Normal", GTK_UI_MANAGER_MENUITEM)
6883         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Low", "Options/Priority/Low", GTK_UI_MANAGER_MENUITEM)
6884         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Priority", "Lowest", "Options/Priority/Lowest", GTK_UI_MANAGER_MENUITEM)
6885
6886         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator3", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6887         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RequestRetRcpt", "Options/RequestRetRcpt", GTK_UI_MANAGER_MENUITEM)
6888         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator4", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6889         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "RemoveReferences", "Options/RemoveReferences", GTK_UI_MANAGER_MENUITEM)
6890         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Separator5", "Options/---", GTK_UI_MANAGER_SEPARATOR)
6891
6892         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options", "Encoding", "Options/Encoding", GTK_UI_MANAGER_MENU)
6893
6894         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_AUTO, "Options/Encoding/"CS_AUTO, GTK_UI_MANAGER_MENUITEM)
6895         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator1", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6896         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_US_ASCII, "Options/Encoding/"CS_US_ASCII, GTK_UI_MANAGER_MENUITEM)
6897         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_UTF_8, "Options/Encoding/"CS_UTF_8, GTK_UI_MANAGER_MENUITEM)
6898         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Separator2", "Options/Encoding/---", GTK_UI_MANAGER_SEPARATOR)
6899
6900         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Western", "Options/Encoding/Western", GTK_UI_MANAGER_MENU)
6901         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)
6902         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)
6903         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Western", CS_WINDOWS_1252, "Options/Encoding/Western/"CS_WINDOWS_1252, GTK_UI_MANAGER_MENUITEM)
6904
6905         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_2, "Options/Encoding/"CS_ISO_8859_2, GTK_UI_MANAGER_MENUITEM)
6906
6907         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Baltic", "Options/Encoding/Baltic", GTK_UI_MANAGER_MENU)
6908         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)
6909         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)
6910
6911         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_7, "Options/Encoding/"CS_ISO_8859_7, GTK_UI_MANAGER_MENUITEM)
6912
6913         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Hebrew", "Options/Encoding/Hebrew", GTK_UI_MANAGER_MENU)
6914         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)
6915         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Hebrew", CS_WINDOWS_1255, "Options/Encoding/Hebrew/"CS_WINDOWS_1255, GTK_UI_MANAGER_MENUITEM)
6916
6917         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Arabic", "Options/Encoding/Arabic", GTK_UI_MANAGER_MENU)
6918         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)
6919         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Arabic", CS_WINDOWS_1256, "Options/Encoding/Arabic/"CS_WINDOWS_1256, GTK_UI_MANAGER_MENUITEM)
6920
6921         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", CS_ISO_8859_9, "Options/Encoding/"CS_ISO_8859_9, GTK_UI_MANAGER_MENUITEM)
6922
6923         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Cyrillic", "Options/Encoding/Cyrillic", GTK_UI_MANAGER_MENU)
6924         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)
6925         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_R, "Options/Encoding/Cyrillic/"CS_KOI8_R, GTK_UI_MANAGER_MENUITEM)
6926         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_KOI8_U, "Options/Encoding/Cyrillic/"CS_KOI8_U, GTK_UI_MANAGER_MENUITEM)
6927         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Cyrillic", CS_WINDOWS_1251, "Options/Encoding/Cyrillic/"CS_WINDOWS_1251, GTK_UI_MANAGER_MENUITEM)
6928
6929         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Japanese", "Options/Encoding/Japanese", GTK_UI_MANAGER_MENU)
6930         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)
6931         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)
6932         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_EUC_JP, "Options/Encoding/Japanese/"CS_EUC_JP, GTK_UI_MANAGER_MENUITEM)
6933         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Japanese", CS_SHIFT_JIS, "Options/Encoding/Japanese/"CS_SHIFT_JIS, GTK_UI_MANAGER_MENUITEM)
6934
6935         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Chinese", "Options/Encoding/Chinese", GTK_UI_MANAGER_MENU)
6936         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GB2312, "Options/Encoding/Chinese/"CS_GB2312, GTK_UI_MANAGER_MENUITEM)
6937         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_GBK, "Options/Encoding/Chinese/"CS_GBK, GTK_UI_MANAGER_MENUITEM)
6938         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_BIG5, "Options/Encoding/Chinese/"CS_BIG5, GTK_UI_MANAGER_MENUITEM)
6939         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Chinese", CS_EUC_TW, "Options/Encoding/Chinese/"CS_EUC_TW, GTK_UI_MANAGER_MENUITEM)
6940
6941         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Korean", "Options/Encoding/Korean", GTK_UI_MANAGER_MENU)
6942         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Korean", CS_EUC_KR, "Options/Encoding/Korean/"CS_EUC_KR, GTK_UI_MANAGER_MENUITEM)
6943         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)
6944
6945         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding", "Thai", "Options/Encoding/Thai", GTK_UI_MANAGER_MENU)
6946         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_TIS_620, "Options/Encoding/Thai/"CS_TIS_620, GTK_UI_MANAGER_MENUITEM)
6947         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Options/Encoding/Thai", CS_WINDOWS_874, "Options/Encoding/Thai/"CS_WINDOWS_874, GTK_UI_MANAGER_MENUITEM)
6948 /* phew. */
6949
6950 /* Tools menu */
6951         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "ShowRuler", "Tools/ShowRuler", GTK_UI_MANAGER_MENUITEM)
6952         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "AddressBook", "Tools/AddressBook", GTK_UI_MANAGER_MENUITEM)
6953         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Template", "Tools/Template", GTK_UI_MANAGER_MENU)
6954         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Template", "PlaceHolder", "Tools/Template/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6955         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools", "Actions", "Tools/Actions", GTK_UI_MANAGER_MENU)
6956         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Tools/Actions", "PlaceHolder", "Tools/Actions/PlaceHolder", GTK_UI_MANAGER_MENUITEM)
6957
6958 /* Help menu */
6959         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Menu/Help", "About", "Help/About", GTK_UI_MANAGER_MENUITEM)
6960
6961         menubar = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu");
6962         gtk_widget_show_all(menubar);
6963
6964         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(compose->ui_manager));
6965 #ifndef MAEMO
6966         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6967 #else
6968         hildon_window_set_menu(HILDON_WINDOW(window), GTK_MENU(menubar));
6969 #endif
6970
6971         if (prefs_common.toolbar_detachable) {
6972                 handlebox = gtk_handle_box_new();
6973         } else {
6974                 handlebox = gtk_hbox_new(FALSE, 0);
6975         }
6976         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6977
6978         gtk_widget_realize(handlebox);
6979 #ifdef MAEMO
6980         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6981                                           (gpointer)compose);
6982 #else
6983         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6984                                           (gpointer)compose);
6985 #endif
6986
6987         vbox2 = gtk_vbox_new(FALSE, 2);
6988         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6989         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6990         
6991         /* Notebook */
6992         notebook = gtk_notebook_new();
6993         gtk_widget_set_size_request(notebook, -1, 130);
6994         gtk_widget_show(notebook);
6995
6996         /* header labels and entries */
6997         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6998                         compose_create_header(compose),
6999                         gtk_label_new_with_mnemonic(_("Hea_der")));
7000         /* attachment list */
7001         attach_hbox = gtk_hbox_new(FALSE, 0);
7002         gtk_widget_show(attach_hbox);
7003         
7004         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
7005         gtk_widget_show(attach_lab1);
7006         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
7007         
7008         attach_lab2 = gtk_label_new("");
7009         gtk_widget_show(attach_lab2);
7010         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
7011         
7012         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7013                         compose_create_attach(compose),
7014                         attach_hbox);
7015         /* Others Tab */
7016         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
7017                         compose_create_others(compose),
7018                         gtk_label_new_with_mnemonic(_("Othe_rs")));
7019
7020         /* Subject */
7021         subject_hbox = gtk_hbox_new(FALSE, 0);
7022         gtk_widget_show(subject_hbox);
7023
7024         subject_frame = gtk_frame_new(NULL);
7025         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
7026         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
7027         gtk_widget_show(subject_frame);
7028
7029         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
7030         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
7031         gtk_widget_show(subject);
7032
7033         label = gtk_label_new(_("Subject:"));
7034         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
7035         gtk_widget_show(label);
7036
7037         subject_entry = gtk_entry_new();
7038         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
7039         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
7040                          G_CALLBACK(compose_grab_focus_cb), compose);
7041         gtk_widget_show(subject_entry);
7042         compose->subject_entry = subject_entry;
7043         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
7044         
7045         edit_vbox = gtk_vbox_new(FALSE, 0);
7046
7047         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
7048
7049         /* ruler */
7050         ruler_hbox = gtk_hbox_new(FALSE, 0);
7051         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
7052
7053         ruler = gtk_shruler_new();
7054         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
7055         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
7056                            BORDER_WIDTH);
7057
7058         /* text widget */
7059         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
7060         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
7061                                        GTK_POLICY_AUTOMATIC,
7062                                        GTK_POLICY_AUTOMATIC);
7063         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
7064                                             GTK_SHADOW_IN);
7065         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
7066         gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
7067
7068         text = gtk_text_view_new();
7069         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
7070         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
7071         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
7072         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7073         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
7074         
7075         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
7076
7077         g_signal_connect_after(G_OBJECT(text), "size_allocate",
7078                                G_CALLBACK(compose_edit_size_alloc),
7079                                ruler);
7080         g_signal_connect(G_OBJECT(buffer), "changed",
7081                          G_CALLBACK(compose_changed_cb), compose);
7082         g_signal_connect(G_OBJECT(text), "grab_focus",
7083                          G_CALLBACK(compose_grab_focus_cb), compose);
7084         g_signal_connect(G_OBJECT(buffer), "insert_text",
7085                          G_CALLBACK(text_inserted), compose);
7086         g_signal_connect(G_OBJECT(text), "button_press_event",
7087                          G_CALLBACK(text_clicked), compose);
7088 #ifndef MAEMO
7089         g_signal_connect(G_OBJECT(text), "popup-menu",
7090                          G_CALLBACK(compose_popup_menu), compose);
7091 #else
7092         gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
7093                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
7094         g_signal_connect(G_OBJECT(text), "tap-and-hold",
7095                          G_CALLBACK(compose_popup_menu), compose);
7096 #endif
7097         g_signal_connect(G_OBJECT(subject_entry), "changed",
7098                          G_CALLBACK(compose_changed_cb), compose);
7099
7100         /* drag and drop */
7101         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
7102                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
7103                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
7104         g_signal_connect(G_OBJECT(text), "drag_data_received",
7105                          G_CALLBACK(compose_insert_drag_received_cb),
7106                          compose);
7107         g_signal_connect(G_OBJECT(text), "drag-drop",
7108                          G_CALLBACK(compose_drag_drop),
7109                          compose);
7110         gtk_widget_show_all(vbox);
7111
7112         /* pane between attach clist and text */
7113         paned = gtk_vpaned_new();
7114         gtk_container_add(GTK_CONTAINER(vbox2), paned);
7115 #ifdef MAEMO
7116         if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
7117                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
7118         else
7119                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
7120 #endif
7121         gtk_paned_add1(GTK_PANED(paned), notebook);
7122         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
7123         gtk_widget_show_all(paned);
7124
7125
7126         if (prefs_common.textfont) {
7127                 PangoFontDescription *font_desc;
7128
7129                 font_desc = pango_font_description_from_string
7130                         (prefs_common.textfont);
7131                 if (font_desc) {
7132                         gtk_widget_modify_font(text, font_desc);
7133                         pango_font_description_free(font_desc);
7134                 }
7135         }
7136
7137         gtk_action_group_add_actions(action_group, compose_popup_entries,
7138                         G_N_ELEMENTS(compose_popup_entries), (gpointer)compose);
7139         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/", "Popup", NULL, GTK_UI_MANAGER_MENUBAR)
7140         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup", "Compose", "Compose", GTK_UI_MANAGER_MENU)
7141         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Add", "Compose/Add", GTK_UI_MANAGER_MENUITEM)
7142         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Remove", "Compose/Remove", GTK_UI_MANAGER_MENUITEM)
7143         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Separator1", "Compose/---", GTK_UI_MANAGER_SEPARATOR)
7144         MENUITEM_ADDUI_MANAGER(compose->ui_manager, "/Popup/Compose", "Properties", "Compose/Properties", GTK_UI_MANAGER_MENUITEM)
7145         
7146         popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtk_ui_manager_get_widget(compose->ui_manager, "/Popup/Compose")));
7147
7148         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
7149         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
7150         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
7151
7152         tmpl_menu = gtk_ui_manager_get_widget(compose->ui_manager, "/Menu/Tools/Template");
7153
7154         undostruct = undo_init(text);
7155         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
7156                                    compose);
7157
7158         address_completion_start(window);
7159
7160         compose->window        = window;
7161         compose->vbox          = vbox;
7162         compose->menubar       = menubar;
7163         compose->handlebox     = handlebox;
7164
7165         compose->vbox2         = vbox2;
7166
7167         compose->paned = paned;
7168
7169         compose->attach_label  = attach_lab2;
7170
7171         compose->notebook      = notebook;
7172         compose->edit_vbox     = edit_vbox;
7173         compose->ruler_hbox    = ruler_hbox;
7174         compose->ruler         = ruler;
7175         compose->scrolledwin   = scrolledwin;
7176         compose->text          = text;
7177
7178         compose->focused_editable = NULL;
7179
7180         compose->popupmenu    = popupmenu;
7181
7182         compose->tmpl_menu = tmpl_menu;
7183
7184         compose->mode = mode;
7185         compose->rmode = mode;
7186
7187         compose->targetinfo = NULL;
7188         compose->replyinfo  = NULL;
7189         compose->fwdinfo    = NULL;
7190
7191         compose->replyto     = NULL;
7192         compose->cc          = NULL;
7193         compose->bcc         = NULL;
7194         compose->followup_to = NULL;
7195
7196         compose->ml_post     = NULL;
7197
7198         compose->inreplyto   = NULL;
7199         compose->references  = NULL;
7200         compose->msgid       = NULL;
7201         compose->boundary    = NULL;
7202
7203         compose->autowrap       = prefs_common.autowrap;
7204
7205         compose->use_signing    = FALSE;
7206         compose->use_encryption = FALSE;
7207         compose->privacy_system = NULL;
7208
7209         compose->modified = FALSE;
7210
7211         compose->return_receipt = FALSE;
7212
7213         compose->to_list        = NULL;
7214         compose->newsgroup_list = NULL;
7215
7216         compose->undostruct = undostruct;
7217
7218         compose->sig_str = NULL;
7219
7220         compose->exteditor_file    = NULL;
7221         compose->exteditor_pid     = -1;
7222         compose->exteditor_tag     = -1;
7223         compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
7224
7225 #if USE_ASPELL
7226         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
7227         if (mode != COMPOSE_REDIRECT) {
7228                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
7229                     strcmp(prefs_common.dictionary, "")) {
7230                         gtkaspell = gtkaspell_new(prefs_common.aspell_path,
7231                                                   prefs_common.dictionary,
7232                                                   prefs_common.alt_dictionary,
7233                                                   conv_get_locale_charset_str(),
7234                                                   prefs_common.misspelled_col,
7235                                                   prefs_common.check_while_typing,
7236                                                   prefs_common.recheck_when_changing_dict,
7237                                                   prefs_common.use_alternate,
7238                                                   prefs_common.use_both_dicts,
7239                                                   GTK_TEXT_VIEW(text),
7240                                                   GTK_WINDOW(compose->window),
7241                                                   compose_spell_menu_changed,
7242                                                   compose);
7243                         if (!gtkaspell) {
7244                                 alertpanel_error(_("Spell checker could not "
7245                                                 "be started.\n%s"),
7246                                                 gtkaspell_checkers_strerror());
7247                                 gtkaspell_checkers_reset_error();
7248                         } else {
7249                                 if (!gtkaspell_set_sug_mode(gtkaspell,
7250                                                 prefs_common.aspell_sugmode)) {
7251                                         debug_print("Aspell: could not set "
7252                                                     "suggestion mode %s\n",
7253                                                     gtkaspell_checkers_strerror());
7254                                         gtkaspell_checkers_reset_error();
7255                                 }
7256
7257                                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", TRUE);
7258                         }
7259                 }
7260         }
7261         compose->gtkaspell = gtkaspell;
7262         compose_spell_menu_changed(compose);
7263 #endif
7264
7265         compose_select_account(compose, account, TRUE);
7266
7267         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", prefs_common.autowrap);
7268
7269         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
7270                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
7271
7272         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
7273                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
7274         
7275         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
7276                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
7277
7278         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", compose->mode == COMPOSE_REPLY);
7279         if (account->protocol != A_NNTP)
7280                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7281                                 prefs_common_translated_header_name("To:"));
7282         else
7283                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN((compose->header_last->combo)))),
7284                                 prefs_common_translated_header_name("Newsgroups:"));
7285
7286         addressbook_set_target_compose(compose);
7287         
7288         if (mode != COMPOSE_REDIRECT)
7289                 compose_set_template_menu(compose);
7290         else {
7291                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Template", FALSE);
7292         }
7293
7294         compose_list = g_list_append(compose_list, compose);
7295
7296         if (!prefs_common.show_ruler)
7297                 gtk_widget_hide(ruler_hbox);
7298                 
7299         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Tools/ShowRuler", prefs_common.show_ruler);
7300
7301         /* Priority */
7302         compose->priority = PRIORITY_NORMAL;
7303         compose_update_priority_menu_item(compose);
7304
7305         compose_set_out_encoding(compose);
7306         
7307         /* Actions menu */
7308         compose_update_actions_menu(compose);
7309
7310         /* Privacy Systems menu */
7311         compose_update_privacy_systems_menu(compose);
7312
7313         activate_privacy_system(compose, account, TRUE);
7314         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
7315         if (batch) {
7316                 gtk_widget_realize(window);
7317         } else {
7318                 gtk_widget_show(window);
7319 #ifdef MAEMO
7320                 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
7321                 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
7322 #endif
7323         }
7324         
7325         return compose;
7326 }
7327
7328 static GtkWidget *compose_account_option_menu_create(Compose *compose)
7329 {
7330         GList *accounts;
7331         GtkWidget *hbox;
7332         GtkWidget *optmenu;
7333         GtkWidget *optmenubox;
7334         GtkListStore *menu;
7335         GtkTreeIter iter;
7336         GtkWidget *from_name = NULL;
7337 #if !(GTK_CHECK_VERSION(2,12,0))
7338         GtkTooltips *tips = compose->tooltips;
7339 #endif
7340
7341         gint num = 0, def_menu = 0;
7342         
7343         accounts = account_get_list();
7344         g_return_val_if_fail(accounts != NULL, NULL);
7345
7346         optmenubox = gtk_event_box_new();
7347         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
7348         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7349
7350         hbox = gtk_hbox_new(FALSE, 6);
7351         from_name = gtk_entry_new();
7352         
7353         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
7354                          G_CALLBACK(compose_grab_focus_cb), compose);
7355
7356         for (; accounts != NULL; accounts = accounts->next, num++) {
7357                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7358                 gchar *name, *from = NULL;
7359
7360                 if (ac == compose->account) def_menu = num;
7361
7362                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7363                                        ac->account_name);
7364                 
7365                 if (ac == compose->account) {
7366                         if (ac->name && *ac->name) {
7367                                 gchar *buf;
7368                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7369                                 from = g_strdup_printf("%s <%s>",
7370                                                        buf, ac->address);
7371                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7372                         } else {
7373                                 from = g_strdup_printf("%s",
7374                                                        ac->address);
7375                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7376                         }
7377                 }
7378                 COMBOBOX_ADD(menu, name, ac->account_id);
7379                 g_free(name);
7380                 g_free(from);
7381         }
7382
7383         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7384
7385         g_signal_connect(G_OBJECT(optmenu), "changed",
7386                         G_CALLBACK(account_activated),
7387                         compose);
7388         g_signal_connect(G_OBJECT(from_name), "populate-popup",
7389                          G_CALLBACK(compose_entry_popup_extend),
7390                          NULL);
7391
7392         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7393         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7394         
7395         CLAWS_SET_TIP(optmenubox,
7396                 _("Account to use for this email"));
7397         CLAWS_SET_TIP(from_name,
7398                 _("Sender address to be used"));
7399
7400         compose->from_name = from_name;
7401         
7402         return hbox;
7403 }
7404
7405 static void compose_set_priority_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7406 {
7407         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7408         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7409         Compose *compose = (Compose *) data;
7410         if (active) {
7411                 compose->priority = value;
7412         }
7413 }
7414
7415 static void compose_reply_change_mode(Compose *compose,
7416                                     ComposeMode action)
7417 {
7418         gboolean was_modified = compose->modified;
7419
7420         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7421         
7422         g_return_if_fail(compose->replyinfo != NULL);
7423         
7424         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7425                 ml = TRUE;
7426         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7427                 followup = TRUE;
7428         if (action == COMPOSE_REPLY_TO_ALL)
7429                 all = TRUE;
7430         if (action == COMPOSE_REPLY_TO_SENDER)
7431                 sender = TRUE;
7432         if (action == COMPOSE_REPLY_TO_LIST)
7433                 ml = TRUE;
7434
7435         compose_remove_header_entries(compose);
7436         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7437         if (compose->account->set_autocc && compose->account->auto_cc)
7438                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7439
7440         if (compose->account->set_autobcc && compose->account->auto_bcc) 
7441                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7442         
7443         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7444                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7445         compose_show_first_last_header(compose, TRUE);
7446         compose->modified = was_modified;
7447         compose_set_title(compose);
7448 }
7449
7450 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
7451 {
7452         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
7453         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
7454         Compose *compose = (Compose *) data;
7455         
7456         if (active)
7457                 compose_reply_change_mode(compose, value);
7458 }
7459
7460 static void compose_update_priority_menu_item(Compose * compose)
7461 {
7462         GtkWidget *menuitem = NULL;
7463         switch (compose->priority) {
7464                 case PRIORITY_HIGHEST:
7465                         menuitem = gtk_ui_manager_get_widget
7466                                 (compose->ui_manager, "/Menu/Options/Priority/Highest");
7467                         break;
7468                 case PRIORITY_HIGH:
7469                         menuitem = gtk_ui_manager_get_widget
7470                                 (compose->ui_manager, "/Menu/Options/Priority/High");
7471                         break;
7472                 case PRIORITY_NORMAL:
7473                         menuitem = gtk_ui_manager_get_widget
7474                                 (compose->ui_manager, "/Menu/Options/Priority/Normal");
7475                         break;
7476                 case PRIORITY_LOW:
7477                         menuitem = gtk_ui_manager_get_widget
7478                                 (compose->ui_manager, "/Menu/Options/Priority/Low");
7479                         break;
7480                 case PRIORITY_LOWEST:
7481                         menuitem = gtk_ui_manager_get_widget
7482                                 (compose->ui_manager, "/Menu/Options/Priority/Lowest");
7483                         break;
7484         }
7485         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7486 }       
7487
7488 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7489 {
7490         Compose *compose = (Compose *) data;
7491         gchar *systemid;
7492         gboolean can_sign = FALSE, can_encrypt = FALSE;
7493
7494         g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7495
7496         if (!GTK_CHECK_MENU_ITEM(widget)->active)
7497                 return;
7498
7499         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7500         g_free(compose->privacy_system);
7501         compose->privacy_system = NULL;
7502         if (systemid != NULL) {
7503                 compose->privacy_system = g_strdup(systemid);
7504
7505                 can_sign = privacy_system_can_sign(systemid);
7506                 can_encrypt = privacy_system_can_encrypt(systemid);
7507         }
7508
7509         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7510
7511         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7512         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7513 }
7514
7515 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7516 {
7517         static gchar *branch_path = "/Menu/Options/PrivacySystem";
7518         GtkWidget *menuitem = NULL;
7519         GList *amenu;
7520         gboolean can_sign = FALSE, can_encrypt = FALSE;
7521         gboolean found = FALSE;
7522
7523         if (compose->privacy_system != NULL) {
7524                 gchar *systemid;
7525                 menuitem = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
7526                                 gtk_ui_manager_get_widget(compose->ui_manager, branch_path)));
7527                 g_return_if_fail(menuitem != NULL);
7528
7529                 amenu = GTK_MENU_SHELL(menuitem)->children;
7530                 menuitem = NULL;
7531                 while (amenu != NULL) {
7532                         GList *alist = amenu->next;
7533
7534                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7535                         if (systemid != NULL) {
7536                                 if (strcmp(systemid, compose->privacy_system) == 0 &&
7537                                     GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7538                                         menuitem = GTK_WIDGET(amenu->data);
7539
7540                                         can_sign = privacy_system_can_sign(systemid);
7541                                         can_encrypt = privacy_system_can_encrypt(systemid);
7542                                         found = TRUE;
7543                                         break;
7544                                 } 
7545                         } else if (strlen(compose->privacy_system) == 0 && 
7546                                    GTK_IS_CHECK_MENU_ITEM(amenu->data)) {
7547                                         menuitem = GTK_WIDGET(amenu->data);
7548
7549                                         can_sign = FALSE;
7550                                         can_encrypt = FALSE;
7551                                         found = TRUE;
7552                                         break;
7553                         }
7554
7555                         amenu = alist;
7556                 }
7557                 if (menuitem != NULL)
7558                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7559                 
7560                 if (warn && !found && strlen(compose->privacy_system)) {
7561                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7562                                   "will not be able to sign or encrypt this message."),
7563                                   compose->privacy_system);
7564                 }
7565         } 
7566
7567         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Sign", can_sign);
7568         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/Encrypt", can_encrypt);
7569 }       
7570  
7571 static void compose_set_out_encoding(Compose *compose)
7572 {
7573         CharSet out_encoding;
7574         const gchar *branch = NULL;
7575         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7576
7577         switch(out_encoding) {
7578                 case C_AUTO: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7579                 case C_US_ASCII: branch = "Menu/Options/Encoding/" CS_US_ASCII; break;
7580                 case C_UTF_8: branch = "Menu/Options/Encoding/" CS_UTF_8; break;
7581                 case C_ISO_8859_2: branch = "Menu/Options/Encoding/" CS_ISO_8859_2; break;
7582                 case C_ISO_8859_7: branch = "Menu/Options/Encoding/" CS_ISO_8859_7; break;
7583                 case C_ISO_8859_9: branch = "Menu/Options/Encoding/" CS_ISO_8859_9; break;
7584                 case C_ISO_8859_1: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_1; break;
7585                 case C_ISO_8859_15: branch = "Menu/Options/Encoding/Western/" CS_ISO_8859_15; break;
7586                 case C_WINDOWS_1252: branch = "Menu/Options/Encoding/Western/" CS_WINDOWS_1252; break;
7587                 case C_ISO_8859_13: branch = "Menu/Options/Encoding/Baltic/" CS_ISO_8859_13; break;
7588                 case C_ISO_8859_4: branch = "Menu/Options/Encoding/Baltic" CS_ISO_8859_4; break;
7589                 case C_ISO_8859_8: branch = "Menu/Options/Encoding/Hebrew/" CS_ISO_8859_8; break;
7590                 case C_WINDOWS_1255: branch = "Menu/Options/Encoding/Hebrew/" CS_WINDOWS_1255; break;
7591                 case C_ISO_8859_6: branch = "Menu/Options/Encoding/Arabic/" CS_ISO_8859_6; break;
7592                 case C_WINDOWS_1256: branch = "Menu/Options/Encoding/Arabic/" CS_WINDOWS_1256; break;
7593                 case C_ISO_8859_5: branch = "Menu/Options/Encoding/Cyrillic/" CS_ISO_8859_5; break;
7594                 case C_KOI8_R: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_R; break;
7595                 case C_KOI8_U: branch = "Menu/Options/Encoding/Cyrillic/" CS_KOI8_U; break;
7596                 case C_WINDOWS_1251: branch = "Menu/Options/Encoding/Cyrillic/" CS_WINDOWS_1251; break;
7597                 case C_ISO_2022_JP: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP; break;
7598                 case C_ISO_2022_JP_2: branch = "Menu/Options/Encoding/Japanese/" CS_ISO_2022_JP_2; break;
7599                 case C_EUC_JP: branch = "Menu/Options/Encoding/Japanese/" CS_EUC_JP; break;
7600                 case C_SHIFT_JIS: branch = "Menu/Options/Encoding/Japanese/" CS_SHIFT_JIS; break;
7601                 case C_GB2312: branch = "Menu/Options/Encoding/Chinese/" CS_GB2312; break;
7602                 case C_GBK: branch = "Menu/Options/Encoding/Chinese/" CS_GBK; break;
7603                 case C_BIG5: branch = "Menu/Options/Encoding/Chinese/" CS_BIG5; break;
7604                 case C_EUC_TW: branch = "Menu/Options/Encoding/Chinese/" CS_EUC_TW; break;
7605                 case C_EUC_KR: branch = "Menu/Options/Encoding/Korean/" CS_EUC_KR; break;
7606                 case C_ISO_2022_KR: branch = "Menu/Options/Encoding/Korean/" CS_ISO_2022_KR; break;
7607                 case C_TIS_620: branch = "Menu/Options/Encoding/Thai/" CS_TIS_620; break;
7608                 case C_WINDOWS_874: branch = "Menu/Options/Encoding/Thai/" CS_WINDOWS_874; break;
7609                 default: branch = "Menu/Options/Encoding/" CS_AUTO; break;
7610         }
7611         cm_toggle_menu_set_active_full(compose->ui_manager, (gchar *)branch, TRUE);
7612 }
7613
7614 static void compose_set_template_menu(Compose *compose)
7615 {
7616         GSList *tmpl_list, *cur;
7617         GtkWidget *menu;
7618         GtkWidget *item;
7619
7620         tmpl_list = template_get_config();
7621
7622         menu = gtk_menu_new();
7623
7624         gtk_menu_set_accel_group (GTK_MENU (menu), 
7625                 gtk_ui_manager_get_accel_group(compose->ui_manager));
7626         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7627                 Template *tmpl = (Template *)cur->data;
7628                 gchar *accel_path = NULL;
7629                 item = gtk_menu_item_new_with_label(tmpl->name);
7630                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7631                 g_signal_connect(G_OBJECT(item), "activate",
7632                                  G_CALLBACK(compose_template_activate_cb),
7633                                  compose);
7634                 g_object_set_data(G_OBJECT(item), "template", tmpl);
7635                 gtk_widget_show(item);
7636                 accel_path = g_strconcat("<ComposeTemplates>" , "/", tmpl->name, NULL);
7637                 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
7638                 g_free(accel_path);
7639         }
7640
7641         gtk_widget_show(menu);
7642         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7643 }
7644
7645 void compose_update_actions_menu(Compose *compose)
7646 {
7647         action_update_compose_menu(compose->ui_manager, "/Menu/Tools/Actions", compose);
7648 }
7649
7650 static void compose_update_privacy_systems_menu(Compose *compose)
7651 {
7652         static gchar *branch_path = "/Menu/Options/PrivacySystem";
7653         GSList *systems, *cur;
7654         GtkWidget *widget;
7655         GtkWidget *system_none;
7656         GSList *group;
7657         GtkWidget *privacy_menuitem = gtk_ui_manager_get_widget(compose->ui_manager, branch_path);
7658         GtkWidget *privacy_menu = gtk_menu_new();
7659
7660         system_none = gtk_radio_menu_item_new_with_mnemonic(NULL, _("_None"));
7661         g_object_set_data_full(G_OBJECT(system_none), "privacy_system", NULL, NULL);
7662
7663         g_signal_connect(G_OBJECT(system_none), "activate",
7664                 G_CALLBACK(compose_set_privacy_system_cb), compose);
7665
7666         gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), system_none);
7667         gtk_widget_show(system_none);
7668
7669         systems = privacy_get_system_ids();
7670         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7671                 gchar *systemid = cur->data;
7672
7673                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7674                 widget = gtk_radio_menu_item_new_with_label(group,
7675                         privacy_system_get_name(systemid));
7676                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7677                                        g_strdup(systemid), g_free);
7678                 g_signal_connect(G_OBJECT(widget), "activate",
7679                         G_CALLBACK(compose_set_privacy_system_cb), compose);
7680
7681                 gtk_menu_shell_append(GTK_MENU_SHELL(privacy_menu), widget);
7682                 gtk_widget_show(widget);
7683                 g_free(systemid);
7684         }
7685         g_slist_free(systems);
7686         gtk_menu_item_set_submenu(GTK_MENU_ITEM(privacy_menuitem), privacy_menu);
7687         gtk_widget_show_all(privacy_menu);
7688         gtk_widget_show_all(privacy_menuitem);
7689 }
7690
7691 void compose_reflect_prefs_all(void)
7692 {
7693         GList *cur;
7694         Compose *compose;
7695
7696         for (cur = compose_list; cur != NULL; cur = cur->next) {
7697                 compose = (Compose *)cur->data;
7698                 compose_set_template_menu(compose);
7699         }
7700 }
7701
7702 void compose_reflect_prefs_pixmap_theme(void)
7703 {
7704         GList *cur;
7705         Compose *compose;
7706
7707         for (cur = compose_list; cur != NULL; cur = cur->next) {
7708                 compose = (Compose *)cur->data;
7709                 toolbar_update(TOOLBAR_COMPOSE, compose);
7710         }
7711 }
7712
7713 static const gchar *compose_quote_char_from_context(Compose *compose)
7714 {
7715         const gchar *qmark = NULL;
7716
7717         g_return_val_if_fail(compose != NULL, NULL);
7718
7719         switch (compose->mode) {
7720                 /* use forward-specific quote char */
7721                 case COMPOSE_FORWARD:
7722                 case COMPOSE_FORWARD_AS_ATTACH:
7723                 case COMPOSE_FORWARD_INLINE:
7724                         if (compose->folder && compose->folder->prefs &&
7725                                         compose->folder->prefs->forward_with_format)
7726                                 qmark = compose->folder->prefs->forward_quotemark;
7727                         else if (compose->account->forward_with_format)
7728                                 qmark = compose->account->forward_quotemark;
7729                         else
7730                                 qmark = prefs_common.fw_quotemark;
7731                         break;
7732
7733                 /* use reply-specific quote char in all other modes */
7734                 default:
7735                         if (compose->folder && compose->folder->prefs &&
7736                                         compose->folder->prefs->reply_with_format)
7737                                 qmark = compose->folder->prefs->reply_quotemark;
7738                         else if (compose->account->reply_with_format)
7739                                 qmark = compose->account->reply_quotemark;
7740                         else
7741                                 qmark = prefs_common.quotemark;
7742                         break;
7743         }
7744
7745         if (qmark == NULL || *qmark == '\0')
7746                 qmark = "> ";
7747
7748         return qmark;
7749 }
7750
7751 static void compose_template_apply(Compose *compose, Template *tmpl,
7752                                    gboolean replace)
7753 {
7754         GtkTextView *text;
7755         GtkTextBuffer *buffer;
7756         GtkTextMark *mark;
7757         GtkTextIter iter;
7758         const gchar *qmark;
7759         gchar *parsed_str = NULL;
7760         gint cursor_pos = 0;
7761         const gchar *err_msg = _("Template body format error at line %d.");
7762         if (!tmpl) return;
7763
7764         /* process the body */
7765
7766         text = GTK_TEXT_VIEW(compose->text);
7767         buffer = gtk_text_view_get_buffer(text);
7768
7769         if (tmpl->value) {
7770                 qmark = compose_quote_char_from_context(compose);
7771
7772                 if (compose->replyinfo != NULL) {
7773
7774                         if (replace)
7775                                 gtk_text_buffer_set_text(buffer, "", -1);
7776                         mark = gtk_text_buffer_get_insert(buffer);
7777                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7778
7779                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7780                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7781
7782                 } else if (compose->fwdinfo != NULL) {
7783
7784                         if (replace)
7785                                 gtk_text_buffer_set_text(buffer, "", -1);
7786                         mark = gtk_text_buffer_get_insert(buffer);
7787                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7788
7789                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7790                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7791
7792                 } else {
7793                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7794
7795                         GtkTextIter start, end;
7796                         gchar *tmp = NULL;
7797
7798                         gtk_text_buffer_get_start_iter(buffer, &start);
7799                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7800                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7801
7802                         /* clear the buffer now */
7803                         if (replace)
7804                                 gtk_text_buffer_set_text(buffer, "", -1);
7805
7806                         parsed_str = compose_quote_fmt(compose, dummyinfo,
7807                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7808                         procmsg_msginfo_free( dummyinfo );
7809
7810                         g_free( tmp );
7811                 } 
7812         } else {
7813                 if (replace)
7814                         gtk_text_buffer_set_text(buffer, "", -1);
7815                 mark = gtk_text_buffer_get_insert(buffer);
7816                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7817         }       
7818
7819         if (replace && parsed_str && compose->account->auto_sig)
7820                 compose_insert_sig(compose, FALSE);
7821
7822         if (replace && parsed_str) {
7823                 gtk_text_buffer_get_start_iter(buffer, &iter);
7824                 gtk_text_buffer_place_cursor(buffer, &iter);
7825         }
7826         
7827         if (parsed_str) {
7828                 cursor_pos = quote_fmt_get_cursor_pos();
7829                 compose->set_cursor_pos = cursor_pos;
7830                 if (cursor_pos == -1)
7831                         cursor_pos = 0;
7832                 gtk_text_buffer_get_start_iter(buffer, &iter);
7833                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7834                 gtk_text_buffer_place_cursor(buffer, &iter);
7835         }
7836
7837         /* process the other fields */
7838
7839         compose_template_apply_fields(compose, tmpl);
7840         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7841         quote_fmt_reset_vartable();
7842         compose_changed_cb(NULL, compose);
7843 }
7844
7845 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7846 {
7847         MsgInfo* dummyinfo = NULL;
7848         MsgInfo *msginfo = NULL;
7849         gchar *buf = NULL;
7850
7851         if (compose->replyinfo != NULL)
7852                 msginfo = compose->replyinfo;
7853         else if (compose->fwdinfo != NULL)
7854                 msginfo = compose->fwdinfo;
7855         else {
7856                 dummyinfo = compose_msginfo_new_from_compose(compose);
7857                 msginfo = dummyinfo;
7858         }
7859
7860         if (tmpl->from && *tmpl->from != '\0') {
7861 #ifdef USE_ASPELL
7862                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7863                                 compose->gtkaspell);
7864 #else
7865                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7866 #endif
7867                 quote_fmt_scan_string(tmpl->from);
7868                 quote_fmt_parse();
7869
7870                 buf = quote_fmt_get_buffer();
7871                 if (buf == NULL) {
7872                         alertpanel_error(_("Template From format error."));
7873                 } else {
7874                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
7875                 }
7876         }
7877
7878         if (tmpl->to && *tmpl->to != '\0') {
7879 #ifdef USE_ASPELL
7880                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7881                                 compose->gtkaspell);
7882 #else
7883                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7884 #endif
7885                 quote_fmt_scan_string(tmpl->to);
7886                 quote_fmt_parse();
7887
7888                 buf = quote_fmt_get_buffer();
7889                 if (buf == NULL) {
7890                         alertpanel_error(_("Template To format error."));
7891                 } else {
7892                         compose_entry_append(compose, buf, COMPOSE_TO);
7893                 }
7894         }
7895
7896         if (tmpl->cc && *tmpl->cc != '\0') {
7897 #ifdef USE_ASPELL
7898                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7899                                 compose->gtkaspell);
7900 #else
7901                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7902 #endif
7903                 quote_fmt_scan_string(tmpl->cc);
7904                 quote_fmt_parse();
7905
7906                 buf = quote_fmt_get_buffer();
7907                 if (buf == NULL) {
7908                         alertpanel_error(_("Template Cc format error."));
7909                 } else {
7910                         compose_entry_append(compose, buf, COMPOSE_CC);
7911                 }
7912         }
7913
7914         if (tmpl->bcc && *tmpl->bcc != '\0') {
7915 #ifdef USE_ASPELL
7916                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7917                                 compose->gtkaspell);
7918 #else
7919                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7920 #endif
7921                 quote_fmt_scan_string(tmpl->bcc);
7922                 quote_fmt_parse();
7923
7924                 buf = quote_fmt_get_buffer();
7925                 if (buf == NULL) {
7926                         alertpanel_error(_("Template Bcc format error."));
7927                 } else {
7928                         compose_entry_append(compose, buf, COMPOSE_BCC);
7929                 }
7930         }
7931
7932         /* process the subject */
7933         if (tmpl->subject && *tmpl->subject != '\0') {
7934 #ifdef USE_ASPELL
7935                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
7936                                 compose->gtkaspell);
7937 #else
7938                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
7939 #endif
7940                 quote_fmt_scan_string(tmpl->subject);
7941                 quote_fmt_parse();
7942
7943                 buf = quote_fmt_get_buffer();
7944                 if (buf == NULL) {
7945                         alertpanel_error(_("Template subject format error."));
7946                 } else {
7947                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7948                 }
7949         }
7950
7951         procmsg_msginfo_free( dummyinfo );
7952 }
7953
7954 static void compose_destroy(Compose *compose)
7955 {
7956         GtkTextBuffer *buffer;
7957         GtkClipboard *clipboard;
7958
7959         compose_list = g_list_remove(compose_list, compose);
7960
7961         if (compose->updating) {
7962                 debug_print("danger, not destroying anything now\n");
7963                 compose->deferred_destroy = TRUE;
7964                 return;
7965         }
7966         /* NOTE: address_completion_end() does nothing with the window
7967          * however this may change. */
7968         address_completion_end(compose->window);
7969
7970         slist_free_strings(compose->to_list);
7971         g_slist_free(compose->to_list);
7972         slist_free_strings(compose->newsgroup_list);
7973         g_slist_free(compose->newsgroup_list);
7974         slist_free_strings(compose->header_list);
7975         g_slist_free(compose->header_list);
7976
7977         procmsg_msginfo_free(compose->targetinfo);
7978         procmsg_msginfo_free(compose->replyinfo);
7979         procmsg_msginfo_free(compose->fwdinfo);
7980
7981         g_free(compose->replyto);
7982         g_free(compose->cc);
7983         g_free(compose->bcc);
7984         g_free(compose->newsgroups);
7985         g_free(compose->followup_to);
7986
7987         g_free(compose->ml_post);
7988
7989         g_free(compose->inreplyto);
7990         g_free(compose->references);
7991         g_free(compose->msgid);
7992         g_free(compose->boundary);
7993
7994         g_free(compose->redirect_filename);
7995         if (compose->undostruct)
7996                 undo_destroy(compose->undostruct);
7997
7998         g_free(compose->sig_str);
7999
8000         g_free(compose->exteditor_file);
8001
8002         g_free(compose->orig_charset);
8003
8004         g_free(compose->privacy_system);
8005
8006         if (addressbook_get_target_compose() == compose)
8007                 addressbook_set_target_compose(NULL);
8008
8009 #if USE_ASPELL
8010         if (compose->gtkaspell) {
8011                 gtkaspell_delete(compose->gtkaspell);
8012                 compose->gtkaspell = NULL;
8013         }
8014 #endif
8015
8016         prefs_common.compose_width = compose->scrolledwin->allocation.width;
8017         prefs_common.compose_height = compose->window->allocation.height;
8018
8019         if (!gtk_widget_get_parent(compose->paned))
8020                 gtk_widget_destroy(compose->paned);
8021         gtk_widget_destroy(compose->popupmenu);
8022
8023         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
8024         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
8025         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
8026
8027         gtk_widget_destroy(compose->window);
8028         toolbar_destroy(compose->toolbar);
8029         g_free(compose->toolbar);
8030         g_mutex_free(compose->mutex);
8031         g_free(compose);
8032 }
8033
8034 static void compose_attach_info_free(AttachInfo *ainfo)
8035 {
8036         g_free(ainfo->file);
8037         g_free(ainfo->content_type);
8038         g_free(ainfo->name);
8039         g_free(ainfo);
8040 }
8041
8042 static void compose_attach_update_label(Compose *compose)
8043 {
8044         GtkTreeIter iter;
8045         gint i = 1;
8046         gchar *text;
8047         GtkTreeModel *model;
8048         
8049         if(compose == NULL)
8050                 return;
8051                 
8052         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
8053         if(!gtk_tree_model_get_iter_first(model, &iter)) {
8054                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
8055                 return;
8056         }
8057         
8058         while(gtk_tree_model_iter_next(model, &iter))
8059                 i++;
8060         
8061         text = g_strdup_printf("(%d)", i);
8062         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
8063         g_free(text);
8064 }
8065
8066 static void compose_attach_remove_selected(GtkAction *action, gpointer data)
8067 {
8068         Compose *compose = (Compose *)data;
8069         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8070         GtkTreeSelection *selection;
8071         GList *sel, *cur;
8072         GtkTreeModel *model;
8073
8074         selection = gtk_tree_view_get_selection(tree_view);
8075         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8076
8077         if (!sel) 
8078                 return;
8079
8080         for (cur = sel; cur != NULL; cur = cur->next) {
8081                 GtkTreePath *path = cur->data;
8082                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
8083                                                 (model, cur->data);
8084                 cur->data = ref;
8085                 gtk_tree_path_free(path);
8086         }
8087
8088         for (cur = sel; cur != NULL; cur = cur->next) {
8089                 GtkTreeRowReference *ref = cur->data;
8090                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
8091                 GtkTreeIter iter;
8092
8093                 if (gtk_tree_model_get_iter(model, &iter, path))
8094                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
8095                 
8096                 gtk_tree_path_free(path);
8097                 gtk_tree_row_reference_free(ref);
8098         }
8099
8100         g_list_free(sel);
8101         compose_attach_update_label(compose);
8102 }
8103
8104 static struct _AttachProperty
8105 {
8106         GtkWidget *window;
8107         GtkWidget *mimetype_entry;
8108         GtkWidget *encoding_optmenu;
8109         GtkWidget *path_entry;
8110         GtkWidget *filename_entry;
8111         GtkWidget *ok_btn;
8112         GtkWidget *cancel_btn;
8113 } attach_prop;
8114
8115 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
8116 {       
8117         gtk_tree_path_free((GtkTreePath *)ptr);
8118 }
8119
8120 static void compose_attach_property(GtkAction *action, gpointer data)
8121 {
8122         Compose *compose = (Compose *)data;
8123         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
8124         AttachInfo *ainfo;
8125         GtkComboBox *optmenu;
8126         GtkTreeSelection *selection;
8127         GList *sel;
8128         GtkTreeModel *model;
8129         GtkTreeIter iter;
8130         GtkTreePath *path;
8131         static gboolean cancelled;
8132
8133         /* only if one selected */
8134         selection = gtk_tree_view_get_selection(tree_view);
8135         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
8136                 return;
8137
8138         sel = gtk_tree_selection_get_selected_rows(selection, &model);
8139         if (!sel)
8140                 return;
8141
8142         path = (GtkTreePath *) sel->data;
8143         gtk_tree_model_get_iter(model, &iter, path);
8144         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
8145         
8146         if (!ainfo) {
8147                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
8148                 g_list_free(sel);
8149                 return;
8150         }               
8151         g_list_free(sel);
8152
8153         if (!attach_prop.window)
8154                 compose_attach_property_create(&cancelled);
8155         gtk_widget_grab_focus(attach_prop.ok_btn);
8156         gtk_widget_show(attach_prop.window);
8157         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
8158
8159         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
8160         if (ainfo->encoding == ENC_UNKNOWN)
8161                 combobox_select_by_data(optmenu, ENC_BASE64);
8162         else
8163                 combobox_select_by_data(optmenu, ainfo->encoding);
8164
8165         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
8166                            ainfo->content_type ? ainfo->content_type : "");
8167         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
8168                            ainfo->file ? ainfo->file : "");
8169         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
8170                            ainfo->name ? ainfo->name : "");
8171
8172         for (;;) {
8173                 const gchar *entry_text;
8174                 gchar *text;
8175                 gchar *cnttype = NULL;
8176                 gchar *file = NULL;
8177                 off_t size = 0;
8178
8179                 cancelled = FALSE;
8180                 gtk_main();
8181
8182                 gtk_widget_hide(attach_prop.window);
8183                 
8184                 if (cancelled) 
8185                         break;
8186
8187                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
8188                 if (*entry_text != '\0') {
8189                         gchar *p;
8190
8191                         text = g_strstrip(g_strdup(entry_text));
8192                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
8193                                 cnttype = g_strdup(text);
8194                                 g_free(text);
8195                         } else {
8196                                 alertpanel_error(_("Invalid MIME type."));
8197                                 g_free(text);
8198                                 continue;
8199                         }
8200                 }
8201
8202                 ainfo->encoding = combobox_get_active_data(optmenu);
8203
8204                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
8205                 if (*entry_text != '\0') {
8206                         if (is_file_exist(entry_text) &&
8207                             (size = get_file_size(entry_text)) > 0)
8208                                 file = g_strdup(entry_text);
8209                         else {
8210                                 alertpanel_error
8211                                         (_("File doesn't exist or is empty."));
8212                                 g_free(cnttype);
8213                                 continue;
8214                         }
8215                 }
8216
8217                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
8218                 if (*entry_text != '\0') {
8219                         g_free(ainfo->name);
8220                         ainfo->name = g_strdup(entry_text);
8221                 }
8222
8223                 if (cnttype) {
8224                         g_free(ainfo->content_type);
8225                         ainfo->content_type = cnttype;
8226                 }
8227                 if (file) {
8228                         g_free(ainfo->file);
8229                         ainfo->file = file;
8230                 }
8231                 if (size)
8232                         ainfo->size = (goffset)size;
8233
8234                 /* update tree store */
8235                 text = to_human_readable(ainfo->size);
8236                 gtk_tree_model_get_iter(model, &iter, path);
8237                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
8238                                    COL_MIMETYPE, ainfo->content_type,
8239                                    COL_SIZE, text,
8240                                    COL_NAME, ainfo->name,
8241                                    -1);
8242                 
8243                 break;
8244         }
8245
8246         gtk_tree_path_free(path);
8247 }
8248
8249 #define SET_LABEL_AND_ENTRY(str, entry, top) \
8250 { \
8251         label = gtk_label_new(str); \
8252         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
8253                          GTK_FILL, 0, 0, 0); \
8254         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
8255  \
8256         entry = gtk_entry_new(); \
8257         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
8258                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
8259 }
8260
8261 static void compose_attach_property_create(gboolean *cancelled)
8262 {
8263         GtkWidget *window;
8264         GtkWidget *vbox;
8265         GtkWidget *table;
8266         GtkWidget *label;
8267         GtkWidget *mimetype_entry;
8268         GtkWidget *hbox;
8269         GtkWidget *optmenu;
8270         GtkListStore *optmenu_menu;
8271         GtkWidget *path_entry;
8272         GtkWidget *filename_entry;
8273         GtkWidget *hbbox;
8274         GtkWidget *ok_btn;
8275         GtkWidget *cancel_btn;
8276         GList     *mime_type_list, *strlist;
8277         GtkTreeIter iter;
8278
8279         debug_print("Creating attach_property window...\n");
8280
8281         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
8282         gtk_widget_set_size_request(window, 480, -1);
8283         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
8284         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
8285         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
8286         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
8287         g_signal_connect(G_OBJECT(window), "delete_event",
8288                          G_CALLBACK(attach_property_delete_event),
8289                          cancelled);
8290         g_signal_connect(G_OBJECT(window), "key_press_event",
8291                          G_CALLBACK(attach_property_key_pressed),
8292                          cancelled);
8293
8294         vbox = gtk_vbox_new(FALSE, 8);
8295         gtk_container_add(GTK_CONTAINER(window), vbox);
8296
8297         table = gtk_table_new(4, 2, FALSE);
8298         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
8299         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
8300         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
8301
8302         label = gtk_label_new(_("MIME type")); 
8303         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
8304                          GTK_FILL, 0, 0, 0); 
8305         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
8306         mimetype_entry = gtk_combo_box_entry_new_text(); 
8307         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
8308                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8309                          
8310         /* stuff with list */
8311         mime_type_list = procmime_get_mime_type_list();
8312         strlist = NULL;
8313         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
8314                 MimeType *type = (MimeType *) mime_type_list->data;
8315                 gchar *tmp;
8316
8317                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
8318
8319                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
8320                         g_free(tmp);
8321                 else
8322                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
8323                                         (GCompareFunc)strcmp2);
8324         }
8325
8326         for (mime_type_list = strlist; mime_type_list != NULL; 
8327                 mime_type_list = mime_type_list->next) {
8328                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
8329                 g_free(mime_type_list->data);
8330         }
8331         g_list_free(strlist);
8332         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
8333         mimetype_entry = gtk_bin_get_child(GTK_BIN((mimetype_entry)));                   
8334
8335         label = gtk_label_new(_("Encoding"));
8336         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
8337                          GTK_FILL, 0, 0, 0);
8338         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
8339
8340         hbox = gtk_hbox_new(FALSE, 0);
8341         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
8342                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
8343
8344         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
8345         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
8346
8347         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
8348         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
8349         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
8350         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
8351         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
8352
8353         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
8354
8355         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
8356         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
8357
8358         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
8359                                       &ok_btn, GTK_STOCK_OK,
8360                                       NULL, NULL);
8361         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
8362         gtk_widget_grab_default(ok_btn);
8363
8364         g_signal_connect(G_OBJECT(ok_btn), "clicked",
8365                          G_CALLBACK(attach_property_ok),
8366                          cancelled);
8367         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
8368                          G_CALLBACK(attach_property_cancel),
8369                          cancelled);
8370
8371         gtk_widget_show_all(vbox);
8372
8373         attach_prop.window           = window;
8374         attach_prop.mimetype_entry   = mimetype_entry;
8375         attach_prop.encoding_optmenu = optmenu;
8376         attach_prop.path_entry       = path_entry;
8377         attach_prop.filename_entry   = filename_entry;
8378         attach_prop.ok_btn           = ok_btn;
8379         attach_prop.cancel_btn       = cancel_btn;
8380 }
8381
8382 #undef SET_LABEL_AND_ENTRY
8383
8384 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8385 {
8386         *cancelled = FALSE;
8387         gtk_main_quit();
8388 }
8389
8390 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8391 {
8392         *cancelled = TRUE;
8393         gtk_main_quit();
8394 }
8395
8396 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8397                                          gboolean *cancelled)
8398 {
8399         *cancelled = TRUE;
8400         gtk_main_quit();
8401
8402         return TRUE;
8403 }
8404
8405 static gboolean attach_property_key_pressed(GtkWidget *widget,
8406                                             GdkEventKey *event,
8407                                             gboolean *cancelled)
8408 {
8409         if (event && event->keyval == GDK_Escape) {
8410                 *cancelled = TRUE;
8411                 gtk_main_quit();
8412         }
8413         return FALSE;
8414 }
8415
8416 static void compose_exec_ext_editor(Compose *compose)
8417 {
8418 #ifdef G_OS_UNIX
8419         gchar *tmp;
8420         pid_t pid;
8421         gint pipe_fds[2];
8422
8423         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8424                               G_DIR_SEPARATOR, compose);
8425
8426         if (pipe(pipe_fds) < 0) {
8427                 perror("pipe");
8428                 g_free(tmp);
8429                 return;
8430         }
8431
8432         if ((pid = fork()) < 0) {
8433                 perror("fork");
8434                 g_free(tmp);
8435                 return;
8436         }
8437
8438         if (pid != 0) {
8439                 /* close the write side of the pipe */
8440                 close(pipe_fds[1]);
8441
8442                 compose->exteditor_file    = g_strdup(tmp);
8443                 compose->exteditor_pid     = pid;
8444
8445                 compose_set_ext_editor_sensitive(compose, FALSE);
8446
8447                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8448                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8449                                                         G_IO_IN,
8450                                                         compose_input_cb,
8451                                                         compose);
8452         } else {        /* process-monitoring process */
8453                 pid_t pid_ed;
8454
8455                 if (setpgid(0, 0))
8456                         perror("setpgid");
8457
8458                 /* close the read side of the pipe */
8459                 close(pipe_fds[0]);
8460
8461                 if (compose_write_body_to_file(compose, tmp) < 0) {
8462                         fd_write_all(pipe_fds[1], "2\n", 2);
8463                         _exit(1);
8464                 }
8465
8466                 pid_ed = compose_exec_ext_editor_real(tmp);
8467                 if (pid_ed < 0) {
8468                         fd_write_all(pipe_fds[1], "1\n", 2);
8469                         _exit(1);
8470                 }
8471
8472                 /* wait until editor is terminated */
8473                 waitpid(pid_ed, NULL, 0);
8474
8475                 fd_write_all(pipe_fds[1], "0\n", 2);
8476
8477                 close(pipe_fds[1]);
8478                 _exit(0);
8479         }
8480
8481         g_free(tmp);
8482 #endif /* G_OS_UNIX */
8483 }
8484
8485 #ifdef G_OS_UNIX
8486 static gint compose_exec_ext_editor_real(const gchar *file)
8487 {
8488         gchar buf[1024];
8489         gchar *p;
8490         gchar **cmdline;
8491         pid_t pid;
8492
8493         g_return_val_if_fail(file != NULL, -1);
8494
8495         if ((pid = fork()) < 0) {
8496                 perror("fork");
8497                 return -1;
8498         }
8499
8500         if (pid != 0) return pid;
8501
8502         /* grandchild process */
8503
8504         if (setpgid(0, getppid()))
8505                 perror("setpgid");
8506
8507         if (prefs_common_get_ext_editor_cmd() &&
8508             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8509             *(p + 1) == 's' && !strchr(p + 2, '%')) {
8510                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8511         } else {
8512                 if (prefs_common_get_ext_editor_cmd())
8513                         g_warning("External editor command line is invalid: '%s'\n",
8514                                   prefs_common_get_ext_editor_cmd());
8515                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8516         }
8517
8518         cmdline = strsplit_with_quote(buf, " ", 1024);
8519         execvp(cmdline[0], cmdline);
8520
8521         perror("execvp");
8522         g_strfreev(cmdline);
8523
8524         _exit(1);
8525 }
8526
8527 static gboolean compose_ext_editor_kill(Compose *compose)
8528 {
8529         pid_t pgid = compose->exteditor_pid * -1;
8530         gint ret;
8531
8532         ret = kill(pgid, 0);
8533
8534         if (ret == 0 || (ret == -1 && EPERM == errno)) {
8535                 AlertValue val;
8536                 gchar *msg;
8537
8538                 msg = g_strdup_printf
8539                         (_("The external editor is still working.\n"
8540                            "Force terminating the process?\n"
8541                            "process group id: %d"), -pgid);
8542                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8543                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8544                         
8545                 g_free(msg);
8546
8547                 if (val == G_ALERTALTERNATE) {
8548                         g_source_remove(compose->exteditor_tag);
8549                         g_io_channel_shutdown(compose->exteditor_ch,
8550                                               FALSE, NULL);
8551                         g_io_channel_unref(compose->exteditor_ch);
8552
8553                         if (kill(pgid, SIGTERM) < 0) perror("kill");
8554                         waitpid(compose->exteditor_pid, NULL, 0);
8555
8556                         g_warning("Terminated process group id: %d", -pgid);
8557                         g_warning("Temporary file: %s",
8558                                   compose->exteditor_file);
8559
8560                         compose_set_ext_editor_sensitive(compose, TRUE);
8561
8562                         g_free(compose->exteditor_file);
8563                         compose->exteditor_file    = NULL;
8564                         compose->exteditor_pid     = -1;
8565                         compose->exteditor_ch      = NULL;
8566                         compose->exteditor_tag     = -1;
8567                 } else
8568                         return FALSE;
8569         }
8570
8571         return TRUE;
8572 }
8573
8574 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8575                                  gpointer data)
8576 {
8577         gchar buf[3] = "3";
8578         Compose *compose = (Compose *)data;
8579         gsize bytes_read;
8580
8581         debug_print(_("Compose: input from monitoring process\n"));
8582
8583         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8584
8585         g_io_channel_shutdown(source, FALSE, NULL);
8586         g_io_channel_unref(source);
8587
8588         waitpid(compose->exteditor_pid, NULL, 0);
8589
8590         if (buf[0] == '0') {            /* success */
8591                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8592                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8593
8594                 gtk_text_buffer_set_text(buffer, "", -1);
8595                 compose_insert_file(compose, compose->exteditor_file);
8596                 compose_changed_cb(NULL, compose);
8597
8598                 if (claws_unlink(compose->exteditor_file) < 0)
8599                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8600         } else if (buf[0] == '1') {     /* failed */
8601                 g_warning("Couldn't exec external editor\n");
8602                 if (claws_unlink(compose->exteditor_file) < 0)
8603                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8604         } else if (buf[0] == '2') {
8605                 g_warning("Couldn't write to file\n");
8606         } else if (buf[0] == '3') {
8607                 g_warning("Pipe read failed\n");
8608         }
8609
8610         compose_set_ext_editor_sensitive(compose, TRUE);
8611
8612         g_free(compose->exteditor_file);
8613         compose->exteditor_file    = NULL;
8614         compose->exteditor_pid     = -1;
8615         compose->exteditor_ch      = NULL;
8616         compose->exteditor_tag     = -1;
8617
8618         return FALSE;
8619 }
8620
8621 static void compose_set_ext_editor_sensitive(Compose *compose,
8622                                              gboolean sensitive)
8623 {
8624         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Send", sensitive);
8625         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/SendLater", sensitive);
8626         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", sensitive);
8627         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", sensitive);
8628         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapPara", sensitive);
8629         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/WrapAllLines", sensitive);
8630         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/ExtEditor", sensitive);
8631
8632         gtk_widget_set_sensitive(compose->text,                       sensitive);
8633         if (compose->toolbar->send_btn)
8634                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
8635         if (compose->toolbar->sendl_btn)
8636                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
8637         if (compose->toolbar->draft_btn)
8638                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
8639         if (compose->toolbar->insert_btn)
8640                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
8641         if (compose->toolbar->sig_btn)
8642                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
8643         if (compose->toolbar->exteditor_btn)
8644                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8645         if (compose->toolbar->linewrap_current_btn)
8646                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8647         if (compose->toolbar->linewrap_all_btn)
8648                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8649 }
8650 #endif /* G_OS_UNIX */
8651
8652 /**
8653  * compose_undo_state_changed:
8654  *
8655  * Change the sensivity of the menuentries undo and redo
8656  **/
8657 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8658                                        gint redo_state, gpointer data)
8659 {
8660         Compose *compose = (Compose *)data;
8661
8662         switch (undo_state) {
8663         case UNDO_STATE_TRUE:
8664                 if (!undostruct->undo_state) {
8665                         undostruct->undo_state = TRUE;
8666                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", TRUE);
8667                 }
8668                 break;
8669         case UNDO_STATE_FALSE:
8670                 if (undostruct->undo_state) {
8671                         undostruct->undo_state = FALSE;
8672                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", FALSE);
8673                 }
8674                 break;
8675         case UNDO_STATE_UNCHANGED:
8676                 break;
8677         case UNDO_STATE_REFRESH:
8678                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
8679                 break;
8680         default:
8681                 g_warning("Undo state not recognized");
8682                 break;
8683         }
8684
8685         switch (redo_state) {
8686         case UNDO_STATE_TRUE:
8687                 if (!undostruct->redo_state) {
8688                         undostruct->redo_state = TRUE;
8689                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", TRUE);
8690                 }
8691                 break;
8692         case UNDO_STATE_FALSE:
8693                 if (undostruct->redo_state) {
8694                         undostruct->redo_state = FALSE;
8695                         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", FALSE);
8696                 }
8697                 break;
8698         case UNDO_STATE_UNCHANGED:
8699                 break;
8700         case UNDO_STATE_REFRESH:
8701                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
8702                 break;
8703         default:
8704                 g_warning("Redo state not recognized");
8705                 break;
8706         }
8707 }
8708
8709 /* callback functions */
8710
8711 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8712  * includes "non-client" (windows-izm) in calculation, so this calculation
8713  * may not be accurate.
8714  */
8715 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8716                                         GtkAllocation *allocation,
8717                                         GtkSHRuler *shruler)
8718 {
8719         if (prefs_common.show_ruler) {
8720                 gint char_width = 0, char_height = 0;
8721                 gint line_width_in_chars;
8722
8723                 gtkut_get_font_size(GTK_WIDGET(widget),
8724                                     &char_width, &char_height);
8725                 line_width_in_chars =
8726                         (allocation->width - allocation->x) / char_width;
8727
8728                 /* got the maximum */
8729                 gtk_ruler_set_range(GTK_RULER(shruler),
8730                                     0.0, line_width_in_chars, 0,
8731                                     /*line_width_in_chars*/ char_width);
8732         }
8733
8734         return TRUE;
8735 }
8736
8737 static void account_activated(GtkComboBox *optmenu, gpointer data)
8738 {
8739         Compose *compose = (Compose *)data;
8740
8741         PrefsAccount *ac;
8742         gchar *folderidentifier;
8743         gint account_id = 0;
8744         GtkTreeModel *menu;
8745         GtkTreeIter iter;
8746
8747         /* Get ID of active account in the combo box */
8748         menu = gtk_combo_box_get_model(optmenu);
8749         gtk_combo_box_get_active_iter(optmenu, &iter);
8750         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8751
8752         ac = account_find_from_id(account_id);
8753         g_return_if_fail(ac != NULL);
8754
8755         if (ac != compose->account)
8756                 compose_select_account(compose, ac, FALSE);
8757
8758         /* Set message save folder */
8759         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8760                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8761         }
8762         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8763                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8764                            
8765         gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8766         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8767                 folderidentifier = folder_item_get_identifier(account_get_special_folder
8768                                   (compose->account, F_OUTBOX));
8769                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8770                 g_free(folderidentifier);
8771         }
8772 }
8773
8774 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8775                             GtkTreeViewColumn *column, Compose *compose)
8776 {
8777         compose_attach_property(NULL, compose);
8778 }
8779
8780 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8781                                       gpointer data)
8782 {
8783         Compose *compose = (Compose *)data;
8784         GtkTreeSelection *attach_selection;
8785         gint attach_nr_selected;
8786         
8787         if (!event) return FALSE;
8788
8789         if (event->button == 3) {
8790                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8791                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8792                         
8793                 if (attach_nr_selected > 0)
8794                 {
8795                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", TRUE);
8796                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", TRUE);
8797                 } else {
8798                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
8799                         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
8800                 }
8801                         
8802                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8803                                NULL, NULL, event->button, event->time);
8804                 return TRUE;                           
8805         }
8806
8807         return FALSE;
8808 }
8809
8810 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8811                                    gpointer data)
8812 {
8813         Compose *compose = (Compose *)data;
8814
8815         if (!event) return FALSE;
8816
8817         switch (event->keyval) {
8818         case GDK_Delete:
8819                 compose_attach_remove_selected(NULL, compose);
8820                 break;
8821         }
8822         return FALSE;
8823 }
8824
8825 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8826 {
8827         toolbar_comp_set_sensitive(compose, allow);
8828         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message", allow);
8829         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", allow);
8830 #if USE_ASPELL
8831         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", allow);
8832 #endif  
8833         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", allow);
8834         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools", allow);
8835         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Help", allow);
8836         
8837         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8838
8839 }
8840
8841 static void compose_send_cb(GtkAction *action, gpointer data)
8842 {
8843         Compose *compose = (Compose *)data;
8844         
8845         if (prefs_common.work_offline && 
8846             !inc_offline_should_override(TRUE,
8847                 _("Claws Mail needs network access in order "
8848                   "to send this email.")))
8849                 return;
8850         
8851         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8852                 g_source_remove(compose->draft_timeout_tag);
8853                 compose->draft_timeout_tag = -1;
8854         }
8855
8856         compose_send(compose);
8857 }
8858
8859 static void compose_send_later_cb(GtkAction *action, gpointer data)
8860 {
8861         Compose *compose = (Compose *)data;
8862         gint val;
8863
8864         inc_lock();
8865         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8866         inc_unlock();
8867
8868         if (!val) {
8869                 compose_close(compose);
8870         } else if (val == -1) {
8871                 alertpanel_error(_("Could not queue message."));
8872         } else if (val == -2) {
8873                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8874         } else if (val == -3) {
8875                 if (privacy_peek_error())
8876                 alertpanel_error(_("Could not queue message for sending:\n\n"
8877                                    "Signature failed: %s"), privacy_get_error());
8878         } else if (val == -4) {
8879                 alertpanel_error(_("Could not queue message for sending:\n\n"
8880                                    "Charset conversion failed."));
8881         } else if (val == -5) {
8882                 alertpanel_error(_("Could not queue message for sending:\n\n"
8883                                    "Couldn't get recipient encryption key."));
8884         } else if (val == -6) {
8885                 /* silent error */
8886         }
8887         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8888 }
8889
8890 #define DRAFTED_AT_EXIT "drafted_at_exit"
8891 static void compose_register_draft(MsgInfo *info)
8892 {
8893         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8894                                       DRAFTED_AT_EXIT, NULL);
8895         FILE *fp = fopen(filepath, "ab");
8896         
8897         if (fp) {
8898                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
8899                                 info->msgnum);
8900                 fclose(fp);
8901         }
8902                 
8903         g_free(filepath);       
8904 }
8905
8906 gboolean compose_draft (gpointer data, guint action) 
8907 {
8908         Compose *compose = (Compose *)data;
8909         FolderItem *draft;
8910         gchar *tmp;
8911         gint msgnum;
8912         MsgFlags flag = {0, 0};
8913         static gboolean lock = FALSE;
8914         MsgInfo *newmsginfo;
8915         FILE *fp;
8916         gboolean target_locked = FALSE;
8917         gboolean err = FALSE;
8918
8919         if (lock) return FALSE;
8920
8921         if (compose->sending)
8922                 return TRUE;
8923
8924         draft = account_get_special_folder(compose->account, F_DRAFT);
8925         g_return_val_if_fail(draft != NULL, FALSE);
8926         
8927         if (!g_mutex_trylock(compose->mutex)) {
8928                 /* we don't want to lock the mutex once it's available,
8929                  * because as the only other part of compose.c locking
8930                  * it is compose_close - which means once unlocked,
8931                  * the compose struct will be freed */
8932                 debug_print("couldn't lock mutex, probably sending\n");
8933                 return FALSE;
8934         }
8935         
8936         lock = TRUE;
8937
8938         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8939                               G_DIR_SEPARATOR, compose);
8940         if ((fp = g_fopen(tmp, "wb")) == NULL) {
8941                 FILE_OP_ERROR(tmp, "fopen");
8942                 goto warn_err;
8943         }
8944
8945         /* chmod for security */
8946         if (change_file_mode_rw(fp, tmp) < 0) {
8947                 FILE_OP_ERROR(tmp, "chmod");
8948                 g_warning("can't change file mode\n");
8949         }
8950
8951         /* Save draft infos */
8952         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8953         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8954
8955         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8956                 gchar *savefolderid;
8957
8958                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8959                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8960                 g_free(savefolderid);
8961         }
8962         if (compose->return_receipt) {
8963                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8964         }
8965         if (compose->privacy_system) {
8966                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8967                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8968                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8969         }
8970
8971         /* Message-ID of message replying to */
8972         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8973                 gchar *folderid;
8974                 
8975                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8976                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8977                 g_free(folderid);
8978         }
8979         /* Message-ID of message forwarding to */
8980         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8981                 gchar *folderid;
8982                 
8983                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8984                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8985                 g_free(folderid);
8986         }
8987
8988         err |= (fprintf(fp, "X-Claws-Auto-Wrapping:%d\n", compose->autowrap) < 0);
8989
8990         /* end of headers */
8991         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8992
8993         if (err) {
8994                 fclose(fp);
8995                 goto warn_err;
8996         }
8997
8998         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8999                 fclose(fp);
9000                 goto warn_err;
9001         }
9002         if (fclose(fp) == EOF) {
9003                 goto warn_err;
9004         }
9005         
9006         if (compose->targetinfo) {
9007                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
9008                 flag.perm_flags = target_locked?MSG_LOCKED:0;
9009         }
9010         flag.tmp_flags = MSG_DRAFT;
9011
9012         folder_item_scan(draft);
9013         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
9014                 MsgInfo *tmpinfo = NULL;
9015                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
9016                 if (compose->msgid) {
9017                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
9018                 }
9019                 if (tmpinfo) {
9020                         msgnum = tmpinfo->msgnum;
9021                         procmsg_msginfo_free(tmpinfo);
9022                         debug_print("got draft msgnum %d from scanning\n", msgnum);
9023                 } else {
9024                         debug_print("didn't get draft msgnum after scanning\n");
9025                 }
9026         } else {
9027                 debug_print("got draft msgnum %d from adding\n", msgnum);
9028         }
9029         if (msgnum < 0) {
9030 warn_err:
9031                 claws_unlink(tmp);
9032                 g_free(tmp);
9033                 if (action != COMPOSE_AUTO_SAVE) {
9034                         if (action != COMPOSE_DRAFT_FOR_EXIT)
9035                                 alertpanel_error(_("Could not save draft."));
9036                         else {
9037                                 AlertValue val;
9038                                 gtkut_window_popup(compose->window);
9039                                 val = alertpanel_full(_("Could not save draft"),
9040                                         _("Could not save draft.\n"
9041                                         "Do you want to cancel exit or discard this email?"),
9042                                           _("_Cancel exit"), _("_Discard email"), NULL,
9043                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
9044                                 if (val == G_ALERTALTERNATE) {
9045                                         lock = FALSE;
9046                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9047                                         compose_close(compose);
9048                                         return TRUE;
9049                                 } else {
9050                                         lock = FALSE;
9051                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
9052                                         return FALSE;
9053                                 }
9054                         }
9055                 }
9056                 goto unlock;
9057         }
9058         g_free(tmp);
9059
9060         if (compose->mode == COMPOSE_REEDIT) {
9061                 compose_remove_reedit_target(compose, TRUE);
9062         }
9063
9064         newmsginfo = folder_item_get_msginfo(draft, msgnum);
9065
9066         if (newmsginfo) {
9067                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
9068                 if (target_locked)
9069                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
9070                 else
9071                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
9072                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
9073                         procmsg_msginfo_set_flags(newmsginfo, 0,
9074                                                   MSG_HAS_ATTACHMENT);
9075
9076                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
9077                         compose_register_draft(newmsginfo);
9078                 }
9079                 procmsg_msginfo_free(newmsginfo);
9080         }
9081         
9082         folder_item_scan(draft);
9083         
9084         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
9085                 lock = FALSE;
9086                 g_mutex_unlock(compose->mutex); /* must be done before closing */
9087                 compose_close(compose);
9088                 return TRUE;
9089         } else {
9090                 struct stat s;
9091                 gchar *path;
9092
9093                 path = folder_item_fetch_msg(draft, msgnum);
9094                 if (path == NULL) {
9095                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
9096                         goto unlock;
9097                 }
9098                 if (g_stat(path, &s) < 0) {
9099                         FILE_OP_ERROR(path, "stat");
9100                         g_free(path);
9101                         goto unlock;
9102                 }
9103                 g_free(path);
9104
9105                 procmsg_msginfo_free(compose->targetinfo);
9106                 compose->targetinfo = procmsg_msginfo_new();
9107                 compose->targetinfo->msgnum = msgnum;
9108                 compose->targetinfo->size = (goffset)s.st_size;
9109                 compose->targetinfo->mtime = s.st_mtime;
9110                 compose->targetinfo->folder = draft;
9111                 if (target_locked)
9112                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
9113                 compose->mode = COMPOSE_REEDIT;
9114                 
9115                 if (action == COMPOSE_AUTO_SAVE) {
9116                         compose->autosaved_draft = compose->targetinfo;
9117                 }
9118                 compose->modified = FALSE;
9119                 compose_set_title(compose);
9120         }
9121 unlock:
9122         lock = FALSE;
9123         g_mutex_unlock(compose->mutex);
9124         return TRUE;
9125 }
9126
9127 void compose_clear_exit_drafts(void)
9128 {
9129         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9130                                       DRAFTED_AT_EXIT, NULL);
9131         if (is_file_exist(filepath))
9132                 claws_unlink(filepath);
9133         
9134         g_free(filepath);
9135 }
9136
9137 void compose_reopen_exit_drafts(void)
9138 {
9139         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
9140                                       DRAFTED_AT_EXIT, NULL);
9141         FILE *fp = fopen(filepath, "rb");
9142         gchar buf[1024];
9143         
9144         if (fp) {
9145                 while (fgets(buf, sizeof(buf), fp)) {
9146                         gchar **parts = g_strsplit(buf, "\t", 2);
9147                         const gchar *folder = parts[0];
9148                         int msgnum = parts[1] ? atoi(parts[1]):-1;
9149                         
9150                         if (folder && *folder && msgnum > -1) {
9151                                 FolderItem *item = folder_find_item_from_identifier(folder);
9152                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
9153                                 if (info)
9154                                         compose_reedit(info, FALSE);
9155                         }
9156                         g_strfreev(parts);
9157                 }       
9158                 fclose(fp);
9159         }       
9160         g_free(filepath);
9161         compose_clear_exit_drafts();
9162 }
9163
9164 static void compose_save_cb(GtkAction *action, gpointer data)
9165 {
9166         compose_draft(data, COMPOSE_KEEP_EDITING);
9167 }
9168
9169 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
9170 {
9171         if (compose && file_list) {
9172                 GList *tmp;
9173
9174                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9175                         gchar *file = (gchar *) tmp->data;
9176                         gchar *utf8_filename = conv_filename_to_utf8(file);
9177                         compose_attach_append(compose, file, utf8_filename, NULL);
9178                         compose_changed_cb(NULL, compose);
9179                         if (free_data) {
9180                         g_free(file);
9181                                 tmp->data = NULL;
9182                         }
9183                         g_free(utf8_filename);
9184                 }
9185         }
9186 }
9187
9188 static void compose_attach_cb(GtkAction *action, gpointer data)
9189 {
9190         Compose *compose = (Compose *)data;
9191         GList *file_list;
9192
9193         if (compose->redirect_filename != NULL)
9194                 return;
9195
9196         file_list = filesel_select_multiple_files_open(_("Select file"));
9197
9198         if (file_list) {
9199                 compose_attach_from_list(compose, file_list, TRUE);
9200                 g_list_free(file_list);
9201         }
9202 }
9203
9204 static void compose_insert_file_cb(GtkAction *action, gpointer data)
9205 {
9206         Compose *compose = (Compose *)data;
9207         GList *file_list;
9208
9209         file_list = filesel_select_multiple_files_open(_("Select file"));
9210
9211         if (file_list) {
9212                 GList *tmp;
9213
9214                 for ( tmp = file_list; tmp; tmp = tmp->next) {
9215                         gchar *file = (gchar *) tmp->data;
9216                         gchar *filedup = g_strdup(file);
9217                         gchar *shortfile = g_path_get_basename(filedup);
9218                         ComposeInsertResult res;
9219
9220                         res = compose_insert_file(compose, file);
9221                         if (res == COMPOSE_INSERT_READ_ERROR) {
9222                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
9223                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
9224                                 alertpanel_error(_("File '%s' contained invalid characters\n"
9225                                                    "for the current encoding, insertion may be incorrect."), shortfile);
9226                         }
9227                         g_free(shortfile);
9228                         g_free(filedup);
9229                         g_free(file);
9230                 }
9231                 g_list_free(file_list);
9232         }
9233 }
9234
9235 static void compose_insert_sig_cb(GtkAction *action, gpointer data)
9236 {
9237         Compose *compose = (Compose *)data;
9238
9239         compose_insert_sig(compose, FALSE);
9240 }
9241
9242 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
9243                               gpointer data)
9244 {
9245         gint x, y;
9246         Compose *compose = (Compose *)data;
9247
9248         gtkut_widget_get_uposition(widget, &x, &y);
9249         prefs_common.compose_x = x;
9250         prefs_common.compose_y = y;
9251
9252         if (compose->sending || compose->updating)
9253                 return TRUE;
9254         compose_close_cb(NULL, compose);
9255         return TRUE;
9256 }
9257
9258 void compose_close_toolbar(Compose *compose)
9259 {
9260         compose_close_cb(NULL, compose);
9261 }
9262
9263 static void compose_close_cb(GtkAction *action, gpointer data)
9264 {
9265         Compose *compose = (Compose *)data;
9266         AlertValue val;
9267
9268 #ifdef G_OS_UNIX
9269         if (compose->exteditor_tag != -1) {
9270                 if (!compose_ext_editor_kill(compose))
9271                         return;
9272         }
9273 #endif
9274
9275         if (compose->modified) {
9276                 if (!g_mutex_trylock(compose->mutex)) {
9277                         /* we don't want to lock the mutex once it's available,
9278                          * because as the only other part of compose.c locking
9279                          * it is compose_close - which means once unlocked,
9280                          * the compose struct will be freed */
9281                         debug_print("couldn't lock mutex, probably sending\n");
9282                         return;
9283                 }
9284                 val = alertpanel(_("Discard message"),
9285                                  _("This message has been modified. Discard it?"),
9286                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
9287                 g_mutex_unlock(compose->mutex);
9288                 switch (val) {
9289                 case G_ALERTDEFAULT:
9290                         if (prefs_common.autosave)
9291                                 compose_remove_draft(compose);                  
9292                         break;
9293                 case G_ALERTALTERNATE:
9294                         compose_draft(data, COMPOSE_QUIT_EDITING);
9295                         return;
9296                 default:
9297                         return;
9298                 }
9299         }
9300
9301         compose_close(compose);
9302 }
9303
9304 static void compose_set_encoding_cb(GtkAction *action, GtkRadioAction *current, gpointer data)
9305 {
9306         gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current));
9307         gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
9308         Compose *compose = (Compose *) data;
9309
9310         if (active)
9311                 compose->out_encoding = (CharSet)value;
9312 }
9313
9314 static void compose_address_cb(GtkAction *action, gpointer data)
9315 {
9316         Compose *compose = (Compose *)data;
9317
9318         addressbook_open(compose);
9319 }
9320
9321 static void about_show_cb(GtkAction *action, gpointer data)
9322 {
9323         about_show();
9324 }
9325
9326 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
9327 {
9328         Compose *compose = (Compose *)data;
9329         Template *tmpl;
9330         gchar *msg;
9331         AlertValue val;
9332
9333         tmpl = g_object_get_data(G_OBJECT(widget), "template");
9334         g_return_if_fail(tmpl != NULL);
9335
9336         msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
9337                               tmpl->name);
9338         val = alertpanel(_("Apply template"), msg,
9339                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
9340         g_free(msg);
9341
9342         if (val == G_ALERTDEFAULT)
9343                 compose_template_apply(compose, tmpl, TRUE);
9344         else if (val == G_ALERTALTERNATE)
9345                 compose_template_apply(compose, tmpl, FALSE);
9346 }
9347
9348 static void compose_ext_editor_cb(GtkAction *action, gpointer data)
9349 {
9350         Compose *compose = (Compose *)data;
9351
9352         compose_exec_ext_editor(compose);
9353 }
9354
9355 static void compose_undo_cb(GtkAction *action, gpointer data)
9356 {
9357         Compose *compose = (Compose *)data;
9358         gboolean prev_autowrap = compose->autowrap;
9359
9360         compose->autowrap = FALSE;
9361         undo_undo(compose->undostruct);
9362         compose->autowrap = prev_autowrap;
9363 }
9364
9365 static void compose_redo_cb(GtkAction *action, gpointer data)
9366 {
9367         Compose *compose = (Compose *)data;
9368         gboolean prev_autowrap = compose->autowrap;
9369         
9370         compose->autowrap = FALSE;
9371         undo_redo(compose->undostruct);
9372         compose->autowrap = prev_autowrap;
9373 }
9374
9375 static void entry_cut_clipboard(GtkWidget *entry)
9376 {
9377         if (GTK_IS_EDITABLE(entry))
9378                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
9379         else if (GTK_IS_TEXT_VIEW(entry))
9380                 gtk_text_buffer_cut_clipboard(
9381                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9382                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9383                         TRUE);
9384 }
9385
9386 static void entry_copy_clipboard(GtkWidget *entry)
9387 {
9388         if (GTK_IS_EDITABLE(entry))
9389                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9390         else if (GTK_IS_TEXT_VIEW(entry))
9391                 gtk_text_buffer_copy_clipboard(
9392                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9393                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9394 }
9395
9396 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
9397                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9398 {
9399         if (GTK_IS_TEXT_VIEW(entry)) {
9400                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9401                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9402                 GtkTextIter start_iter, end_iter;
9403                 gint start, end;
9404                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9405
9406                 if (contents == NULL)
9407                         return;
9408         
9409                 /* we shouldn't delete the selection when middle-click-pasting, or we
9410                  * can't mid-click-paste our own selection */
9411                 if (clip != GDK_SELECTION_PRIMARY) {
9412                         undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9413                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9414                 }
9415                 
9416                 if (insert_place == NULL) {
9417                         /* if insert_place isn't specified, insert at the cursor.
9418                          * used for Ctrl-V pasting */
9419                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9420                         start = gtk_text_iter_get_offset(&start_iter);
9421                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9422                 } else {
9423                         /* if insert_place is specified, paste here.
9424                          * used for mid-click-pasting */
9425                         start = gtk_text_iter_get_offset(insert_place);
9426                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9427                 }
9428                 
9429                 if (!wrap) {
9430                         /* paste unwrapped: mark the paste so it's not wrapped later */
9431                         end = start + strlen(contents);
9432                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9433                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9434                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9435                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9436                         /* rewrap paragraph now (after a mid-click-paste) */
9437                         mark_start = gtk_text_buffer_get_insert(buffer);
9438                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9439                         gtk_text_iter_backward_char(&start_iter);
9440                         compose_beautify_paragraph(compose, &start_iter, TRUE);
9441                 }
9442         } else if (GTK_IS_EDITABLE(entry))
9443                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9444
9445         compose->modified = TRUE;
9446 }
9447
9448 static void entry_allsel(GtkWidget *entry)
9449 {
9450         if (GTK_IS_EDITABLE(entry))
9451                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9452         else if (GTK_IS_TEXT_VIEW(entry)) {
9453                 GtkTextIter startiter, enditer;
9454                 GtkTextBuffer *textbuf;
9455
9456                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9457                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9458                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9459
9460                 gtk_text_buffer_move_mark_by_name(textbuf, 
9461                         "selection_bound", &startiter);
9462                 gtk_text_buffer_move_mark_by_name(textbuf, 
9463                         "insert", &enditer);
9464         }
9465 }
9466
9467 static void compose_cut_cb(GtkAction *action, gpointer data)
9468 {
9469         Compose *compose = (Compose *)data;
9470         if (compose->focused_editable 
9471 #ifndef GENERIC_UMPC
9472             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9473 #endif
9474             )
9475                 entry_cut_clipboard(compose->focused_editable);
9476 }
9477
9478 static void compose_copy_cb(GtkAction *action, gpointer data)
9479 {
9480         Compose *compose = (Compose *)data;
9481         if (compose->focused_editable 
9482 #ifndef GENERIC_UMPC
9483             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9484 #endif
9485             )
9486                 entry_copy_clipboard(compose->focused_editable);
9487 }
9488
9489 static void compose_paste_cb(GtkAction *action, gpointer data)
9490 {
9491         Compose *compose = (Compose *)data;
9492         gint prev_autowrap;
9493         GtkTextBuffer *buffer;
9494         BLOCK_WRAP();
9495         if (compose->focused_editable &&
9496             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9497                 entry_paste_clipboard(compose, compose->focused_editable, 
9498                                 prefs_common.linewrap_pastes,
9499                                 GDK_SELECTION_CLIPBOARD, NULL);
9500         UNBLOCK_WRAP();
9501 }
9502
9503 static void compose_paste_as_quote_cb(GtkAction *action, gpointer data)
9504 {
9505         Compose *compose = (Compose *)data;
9506         gint wrap_quote = prefs_common.linewrap_quote;
9507         if (compose->focused_editable 
9508 #ifndef GENERIC_UMPC
9509             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9510 #endif
9511             ) {
9512                 /* let text_insert() (called directly or at a later time
9513                  * after the gtk_editable_paste_clipboard) know that 
9514                  * text is to be inserted as a quotation. implemented
9515                  * by using a simple refcount... */
9516                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9517                                                 G_OBJECT(compose->focused_editable),
9518                                                 "paste_as_quotation"));
9519                 g_object_set_data(G_OBJECT(compose->focused_editable),
9520                                     "paste_as_quotation",
9521                                     GINT_TO_POINTER(paste_as_quotation + 1));
9522                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9523                 entry_paste_clipboard(compose, compose->focused_editable, 
9524                                 prefs_common.linewrap_pastes,
9525                                 GDK_SELECTION_CLIPBOARD, NULL);
9526                 prefs_common.linewrap_quote = wrap_quote;
9527         }
9528 }
9529
9530 static void compose_paste_no_wrap_cb(GtkAction *action, gpointer data)
9531 {
9532         Compose *compose = (Compose *)data;
9533         gint prev_autowrap;
9534         GtkTextBuffer *buffer;
9535         BLOCK_WRAP();
9536         if (compose->focused_editable 
9537 #ifndef GENERIC_UMPC
9538             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9539 #endif
9540             )
9541                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9542                         GDK_SELECTION_CLIPBOARD, NULL);
9543         UNBLOCK_WRAP();
9544 }
9545
9546 static void compose_paste_wrap_cb(GtkAction *action, gpointer data)
9547 {
9548         Compose *compose = (Compose *)data;
9549         gint prev_autowrap;
9550         GtkTextBuffer *buffer;
9551         BLOCK_WRAP();
9552         if (compose->focused_editable 
9553 #ifndef GENERIC_UMPC
9554             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9555 #endif
9556             )
9557                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9558                         GDK_SELECTION_CLIPBOARD, NULL);
9559         UNBLOCK_WRAP();
9560 }
9561
9562 static void compose_allsel_cb(GtkAction *action, gpointer data)
9563 {
9564         Compose *compose = (Compose *)data;
9565         if (compose->focused_editable 
9566 #ifndef GENERIC_UMPC
9567             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9568 #endif
9569             )
9570                 entry_allsel(compose->focused_editable);
9571 }
9572
9573 static void textview_move_beginning_of_line (GtkTextView *text)
9574 {
9575         GtkTextBuffer *buffer;
9576         GtkTextMark *mark;
9577         GtkTextIter ins;
9578
9579         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9580
9581         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9582         mark = gtk_text_buffer_get_insert(buffer);
9583         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9584         gtk_text_iter_set_line_offset(&ins, 0);
9585         gtk_text_buffer_place_cursor(buffer, &ins);
9586 }
9587
9588 static void textview_move_forward_character (GtkTextView *text)
9589 {
9590         GtkTextBuffer *buffer;
9591         GtkTextMark *mark;
9592         GtkTextIter ins;
9593
9594         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9595
9596         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9597         mark = gtk_text_buffer_get_insert(buffer);
9598         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9599         if (gtk_text_iter_forward_cursor_position(&ins))
9600                 gtk_text_buffer_place_cursor(buffer, &ins);
9601 }
9602
9603 static void textview_move_backward_character (GtkTextView *text)
9604 {
9605         GtkTextBuffer *buffer;
9606         GtkTextMark *mark;
9607         GtkTextIter ins;
9608
9609         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9610
9611         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9612         mark = gtk_text_buffer_get_insert(buffer);
9613         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9614         if (gtk_text_iter_backward_cursor_position(&ins))
9615                 gtk_text_buffer_place_cursor(buffer, &ins);
9616 }
9617
9618 static void textview_move_forward_word (GtkTextView *text)
9619 {
9620         GtkTextBuffer *buffer;
9621         GtkTextMark *mark;
9622         GtkTextIter ins;
9623         gint count;
9624
9625         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9626
9627         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9628         mark = gtk_text_buffer_get_insert(buffer);
9629         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9630         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9631         if (gtk_text_iter_forward_word_ends(&ins, count)) {
9632                 gtk_text_iter_backward_word_start(&ins);
9633                 gtk_text_buffer_place_cursor(buffer, &ins);
9634         }
9635 }
9636
9637 static void textview_move_backward_word (GtkTextView *text)
9638 {
9639         GtkTextBuffer *buffer;
9640         GtkTextMark *mark;
9641         GtkTextIter ins;
9642         gint count;
9643
9644         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9645
9646         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9647         mark = gtk_text_buffer_get_insert(buffer);
9648         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9649         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9650         if (gtk_text_iter_backward_word_starts(&ins, 1))
9651                 gtk_text_buffer_place_cursor(buffer, &ins);
9652 }
9653
9654 static void textview_move_end_of_line (GtkTextView *text)
9655 {
9656         GtkTextBuffer *buffer;
9657         GtkTextMark *mark;
9658         GtkTextIter ins;
9659
9660         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9661
9662         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9663         mark = gtk_text_buffer_get_insert(buffer);
9664         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9665         if (gtk_text_iter_forward_to_line_end(&ins))
9666                 gtk_text_buffer_place_cursor(buffer, &ins);
9667 }
9668
9669 static void textview_move_next_line (GtkTextView *text)
9670 {
9671         GtkTextBuffer *buffer;
9672         GtkTextMark *mark;
9673         GtkTextIter ins;
9674         gint offset;
9675
9676         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9677
9678         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9679         mark = gtk_text_buffer_get_insert(buffer);
9680         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9681         offset = gtk_text_iter_get_line_offset(&ins);
9682         if (gtk_text_iter_forward_line(&ins)) {
9683                 gtk_text_iter_set_line_offset(&ins, offset);
9684                 gtk_text_buffer_place_cursor(buffer, &ins);
9685         }
9686 }
9687
9688 static void textview_move_previous_line (GtkTextView *text)
9689 {
9690         GtkTextBuffer *buffer;
9691         GtkTextMark *mark;
9692         GtkTextIter ins;
9693         gint offset;
9694
9695         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9696
9697         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9698         mark = gtk_text_buffer_get_insert(buffer);
9699         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9700         offset = gtk_text_iter_get_line_offset(&ins);
9701         if (gtk_text_iter_backward_line(&ins)) {
9702                 gtk_text_iter_set_line_offset(&ins, offset);
9703                 gtk_text_buffer_place_cursor(buffer, &ins);
9704         }
9705 }
9706
9707 static void textview_delete_forward_character (GtkTextView *text)
9708 {
9709         GtkTextBuffer *buffer;
9710         GtkTextMark *mark;
9711         GtkTextIter ins, end_iter;
9712
9713         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9714
9715         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9716         mark = gtk_text_buffer_get_insert(buffer);
9717         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9718         end_iter = ins;
9719         if (gtk_text_iter_forward_char(&end_iter)) {
9720                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9721         }
9722 }
9723
9724 static void textview_delete_backward_character (GtkTextView *text)
9725 {
9726         GtkTextBuffer *buffer;
9727         GtkTextMark *mark;
9728         GtkTextIter ins, end_iter;
9729
9730         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9731
9732         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9733         mark = gtk_text_buffer_get_insert(buffer);
9734         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9735         end_iter = ins;
9736         if (gtk_text_iter_backward_char(&end_iter)) {
9737                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9738         }
9739 }
9740
9741 static void textview_delete_forward_word (GtkTextView *text)
9742 {
9743         GtkTextBuffer *buffer;
9744         GtkTextMark *mark;
9745         GtkTextIter ins, end_iter;
9746
9747         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9748
9749         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9750         mark = gtk_text_buffer_get_insert(buffer);
9751         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9752         end_iter = ins;
9753         if (gtk_text_iter_forward_word_end(&end_iter)) {
9754                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9755         }
9756 }
9757
9758 static void textview_delete_backward_word (GtkTextView *text)
9759 {
9760         GtkTextBuffer *buffer;
9761         GtkTextMark *mark;
9762         GtkTextIter ins, end_iter;
9763
9764         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9765
9766         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9767         mark = gtk_text_buffer_get_insert(buffer);
9768         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9769         end_iter = ins;
9770         if (gtk_text_iter_backward_word_start(&end_iter)) {
9771                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9772         }
9773 }
9774
9775 static void textview_delete_line (GtkTextView *text)
9776 {
9777         GtkTextBuffer *buffer;
9778         GtkTextMark *mark;
9779         GtkTextIter ins, start_iter, end_iter;
9780
9781         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9782
9783         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9784         mark = gtk_text_buffer_get_insert(buffer);
9785         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9786
9787         start_iter = ins;
9788         gtk_text_iter_set_line_offset(&start_iter, 0);
9789
9790         end_iter = ins;
9791         if (gtk_text_iter_ends_line(&end_iter)){
9792                 if (!gtk_text_iter_forward_char(&end_iter))
9793                         gtk_text_iter_backward_char(&start_iter);
9794         }
9795         else 
9796                 gtk_text_iter_forward_to_line_end(&end_iter);
9797         gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9798 }
9799
9800 static void textview_delete_to_line_end (GtkTextView *text)
9801 {
9802         GtkTextBuffer *buffer;
9803         GtkTextMark *mark;
9804         GtkTextIter ins, end_iter;
9805
9806         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9807
9808         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9809         mark = gtk_text_buffer_get_insert(buffer);
9810         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9811         end_iter = ins;
9812         if (gtk_text_iter_ends_line(&end_iter))
9813                 gtk_text_iter_forward_char(&end_iter);
9814         else
9815                 gtk_text_iter_forward_to_line_end(&end_iter);
9816         gtk_text_buffer_delete(buffer, &ins, &end_iter);
9817 }
9818
9819 #define DO_ACTION(name, act) {                                          \
9820         if(!strcmp(name, a_name)) {                                     \
9821                 return act;                                             \
9822         }                                                               \
9823 }
9824 static ComposeCallAdvancedAction compose_call_advanced_action_from_path(GtkAction *action)
9825 {
9826         const gchar *a_name = gtk_action_get_name(action);
9827         DO_ACTION("Edit/Advanced/BackChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER);
9828         DO_ACTION("Edit/Advanced/ForwChar", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER);
9829         DO_ACTION("Edit/Advanced/BackWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD);
9830         DO_ACTION("Edit/Advanced/ForwWord", COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD);
9831         DO_ACTION("Edit/Advanced/BegLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE);
9832         DO_ACTION("Edit/Advanced/EndLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE);
9833         DO_ACTION("Edit/Advanced/PrevLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE);
9834         DO_ACTION("Edit/Advanced/NextLine", COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE);
9835         DO_ACTION("Edit/Advanced/DelBackChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER);
9836         DO_ACTION("Edit/Advanced/DelForwChar", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER);
9837         DO_ACTION("Edit/Advanced/DelBackWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD);
9838         DO_ACTION("Edit/Advanced/DelForwWord", COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD);
9839         DO_ACTION("Edit/Advanced/DelLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE);
9840         DO_ACTION("Edit/Advanced/DelEndLine", COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END);
9841         return -1;
9842 }
9843
9844 static void compose_advanced_action_cb(GtkAction *gaction, gpointer data)
9845 {
9846         Compose *compose = (Compose *)data;
9847         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9848         ComposeCallAdvancedAction action = -1;
9849         
9850         action = compose_call_advanced_action_from_path(gaction);
9851
9852         static struct {
9853                 void (*do_action) (GtkTextView *text);
9854         } action_table[] = {
9855                 {textview_move_beginning_of_line},
9856                 {textview_move_forward_character},
9857                 {textview_move_backward_character},
9858                 {textview_move_forward_word},
9859                 {textview_move_backward_word},
9860                 {textview_move_end_of_line},
9861                 {textview_move_next_line},
9862                 {textview_move_previous_line},
9863                 {textview_delete_forward_character},
9864                 {textview_delete_backward_character},
9865                 {textview_delete_forward_word},
9866                 {textview_delete_backward_word},
9867                 {textview_delete_line},
9868                 {textview_delete_to_line_end}
9869         };
9870
9871         if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9872
9873         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9874             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9875                 if (action_table[action].do_action)
9876                         action_table[action].do_action(text);
9877                 else
9878                         g_warning("Not implemented yet.");
9879         }
9880 }
9881
9882 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9883 {
9884         gchar *str = NULL;
9885         
9886         if (GTK_IS_EDITABLE(widget)) {
9887                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9888                 gtk_editable_set_position(GTK_EDITABLE(widget), 
9889                         strlen(str));
9890                 g_free(str);
9891                 if (widget->parent && widget->parent->parent
9892                  && widget->parent->parent->parent) {
9893                         if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9894                                 gint y = widget->allocation.y;
9895                                 gint height = widget->allocation.height;
9896                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9897                                         (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9898
9899                                 if (y < (int)shown->value) {
9900                                         gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9901                                 }
9902                                 if (y + height > (int)shown->value + (int)shown->page_size) {
9903                                         if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9904                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9905                                                         y + height - (int)shown->page_size - 1);
9906                                         } else {
9907                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9908                                                         (int)shown->upper - (int)shown->page_size - 1);
9909                                         }
9910                                 }
9911                         }
9912                 }
9913         }
9914
9915         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9916                 compose->focused_editable = widget;
9917         
9918 #ifdef GENERIC_UMPC
9919         if (GTK_IS_TEXT_VIEW(widget) 
9920             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9921                 g_object_ref(compose->notebook);
9922                 g_object_ref(compose->edit_vbox);
9923                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9924                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9925                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9926                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9927                 g_object_unref(compose->notebook);
9928                 g_object_unref(compose->edit_vbox);
9929                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9930                                         G_CALLBACK(compose_grab_focus_cb),
9931                                         compose);
9932                 gtk_widget_grab_focus(widget);
9933                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9934                                         G_CALLBACK(compose_grab_focus_cb),
9935                                         compose);
9936         } else if (!GTK_IS_TEXT_VIEW(widget) 
9937                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9938                 g_object_ref(compose->notebook);
9939                 g_object_ref(compose->edit_vbox);
9940                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9941                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9942                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9943                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9944                 g_object_unref(compose->notebook);
9945                 g_object_unref(compose->edit_vbox);
9946                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9947                                         G_CALLBACK(compose_grab_focus_cb),
9948                                         compose);
9949                 gtk_widget_grab_focus(widget);
9950                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9951                                         G_CALLBACK(compose_grab_focus_cb),
9952                                         compose);
9953         }
9954 #endif
9955 }
9956
9957 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9958 {
9959         compose->modified = TRUE;
9960 #ifndef GENERIC_UMPC
9961         compose_set_title(compose);
9962 #endif
9963 }
9964
9965 static void compose_wrap_cb(GtkAction *action, gpointer data)
9966 {
9967         Compose *compose = (Compose *)data;
9968         compose_beautify_paragraph(compose, NULL, TRUE);
9969 }
9970
9971 static void compose_wrap_all_cb(GtkAction *action, gpointer data)
9972 {
9973         Compose *compose = (Compose *)data;
9974         compose_wrap_all_full(compose, TRUE);
9975 }
9976
9977 static void compose_find_cb(GtkAction *action, gpointer data)
9978 {
9979         Compose *compose = (Compose *)data;
9980
9981         message_search_compose(compose);
9982 }
9983
9984 static void compose_toggle_autowrap_cb(GtkToggleAction *action,
9985                                          gpointer        data)
9986 {
9987         Compose *compose = (Compose *)data;
9988         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9989         if (compose->autowrap)
9990                 compose_wrap_all_full(compose, TRUE);
9991         compose->autowrap = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9992 }
9993
9994 static void compose_toggle_sign_cb(GtkToggleAction *action, gpointer data)
9995 {
9996         Compose *compose = (Compose *)data;
9997
9998         compose->use_signing = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
9999 }
10000
10001 static void compose_toggle_encrypt_cb(GtkToggleAction *action, gpointer data)
10002 {
10003         Compose *compose = (Compose *)data;
10004
10005         compose->use_encryption = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
10006 }
10007
10008 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
10009 {
10010         g_free(compose->privacy_system);
10011
10012         compose->privacy_system = g_strdup(account->default_privacy_system);
10013         compose_update_privacy_system_menu_item(compose, warn);
10014 }
10015
10016 static void compose_toggle_ruler_cb(GtkToggleAction *action, gpointer data)
10017 {
10018         Compose *compose = (Compose *)data;
10019
10020         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
10021                 gtk_widget_show(compose->ruler_hbox);
10022                 prefs_common.show_ruler = TRUE;
10023         } else {
10024                 gtk_widget_hide(compose->ruler_hbox);
10025                 gtk_widget_queue_resize(compose->edit_vbox);
10026                 prefs_common.show_ruler = FALSE;
10027         }
10028 }
10029
10030 static void compose_attach_drag_received_cb (GtkWidget          *widget,
10031                                              GdkDragContext     *context,
10032                                              gint                x,
10033                                              gint                y,
10034                                              GtkSelectionData   *data,
10035                                              guint               info,
10036                                              guint               time,
10037                                              gpointer            user_data)
10038 {
10039         Compose *compose = (Compose *)user_data;
10040         GList *list, *tmp;
10041
10042         if (gdk_atom_name(data->type) && 
10043             !strcmp(gdk_atom_name(data->type), "text/uri-list")
10044             && gtk_drag_get_source_widget(context) != 
10045                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10046                 list = uri_list_extract_filenames((const gchar *)data->data);
10047                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10048                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
10049                         compose_attach_append
10050                                 (compose, (const gchar *)tmp->data,
10051                                  utf8_filename, NULL);
10052                         g_free(utf8_filename);
10053                 }
10054                 if (list) compose_changed_cb(NULL, compose);
10055                 list_free_strings(list);
10056                 g_list_free(list);
10057         } else if (gtk_drag_get_source_widget(context) 
10058                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
10059                 /* comes from our summaryview */
10060                 SummaryView * summaryview = NULL;
10061                 GSList * list = NULL, *cur = NULL;
10062                 
10063                 if (mainwindow_get_mainwindow())
10064                         summaryview = mainwindow_get_mainwindow()->summaryview;
10065                 
10066                 if (summaryview)
10067                         list = summary_get_selected_msg_list(summaryview);
10068                 
10069                 for (cur = list; cur; cur = cur->next) {
10070                         MsgInfo *msginfo = (MsgInfo *)cur->data;
10071                         gchar *file = NULL;
10072                         if (msginfo)
10073                                 file = procmsg_get_message_file_full(msginfo, 
10074                                         TRUE, TRUE);
10075                         if (file) {
10076                                 compose_attach_append(compose, (const gchar *)file, 
10077                                         (const gchar *)file, "message/rfc822");
10078                                 g_free(file);
10079                         }
10080                 }
10081                 g_slist_free(list);
10082         }
10083 }
10084
10085 static gboolean compose_drag_drop(GtkWidget *widget,
10086                                   GdkDragContext *drag_context,
10087                                   gint x, gint y,
10088                                   guint time, gpointer user_data)
10089 {
10090         /* not handling this signal makes compose_insert_drag_received_cb
10091          * called twice */
10092         return TRUE;                                     
10093 }
10094
10095 static void compose_insert_drag_received_cb (GtkWidget          *widget,
10096                                              GdkDragContext     *drag_context,
10097                                              gint                x,
10098                                              gint                y,
10099                                              GtkSelectionData   *data,
10100                                              guint               info,
10101                                              guint               time,
10102                                              gpointer            user_data)
10103 {
10104         Compose *compose = (Compose *)user_data;
10105         GList *list, *tmp;
10106
10107         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
10108          * does not work */
10109         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
10110                 AlertValue val = G_ALERTDEFAULT;
10111
10112                 list = uri_list_extract_filenames((const gchar *)data->data);
10113
10114                 if (list == NULL && strstr((gchar *)(data->data), "://")) {
10115                         /* Assume a list of no files, and data has ://, is a remote link */
10116                         gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
10117                         gchar *tmpfile = get_tmp_file();
10118                         str_write_to_file(tmpdata, tmpfile);
10119                         g_free(tmpdata);  
10120                         compose_insert_file(compose, tmpfile);
10121                         claws_unlink(tmpfile);
10122                         g_free(tmpfile);
10123                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10124                         compose_beautify_paragraph(compose, NULL, TRUE);
10125                         return;
10126                 }
10127                 switch (prefs_common.compose_dnd_mode) {
10128                         case COMPOSE_DND_ASK:
10129                                 val = alertpanel_full(_("Insert or attach?"),
10130                                          _("Do you want to insert the contents of the file(s) "
10131                                            "into the message body, or attach it to the email?"),
10132                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
10133                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
10134                                 break;
10135                         case COMPOSE_DND_INSERT:
10136                                 val = G_ALERTALTERNATE;
10137                                 break;
10138                         case COMPOSE_DND_ATTACH:
10139                                 val = G_ALERTOTHER;
10140                                 break;
10141                         default:
10142                                 /* unexpected case */
10143                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
10144                 }
10145
10146                 if (val & G_ALERTDISABLE) {
10147                         val &= ~G_ALERTDISABLE;
10148                         /* remember what action to perform by default, only if we don't click Cancel */
10149                         if (val == G_ALERTALTERNATE)
10150                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
10151                         else if (val == G_ALERTOTHER)
10152                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
10153                 }
10154
10155                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
10156                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
10157                         list_free_strings(list);
10158                         g_list_free(list);
10159                         return;
10160                 } else if (val == G_ALERTOTHER) {
10161                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
10162                         list_free_strings(list);
10163                         g_list_free(list);
10164                         return;
10165                 } 
10166
10167                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
10168                         compose_insert_file(compose, (const gchar *)tmp->data);
10169                 }
10170                 list_free_strings(list);
10171                 g_list_free(list);
10172                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10173                 return;
10174         } else {
10175 #if GTK_CHECK_VERSION(2, 8, 0)
10176                 /* do nothing, handled by GTK */
10177 #else
10178                 gchar *tmpfile = get_tmp_file();
10179                 str_write_to_file((const gchar *)data->data, tmpfile);
10180                 compose_insert_file(compose, tmpfile);
10181                 claws_unlink(tmpfile);
10182                 g_free(tmpfile);
10183                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10184 #endif
10185                 return;
10186         }
10187         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10188 }
10189
10190 static void compose_header_drag_received_cb (GtkWidget          *widget,
10191                                              GdkDragContext     *drag_context,
10192                                              gint                x,
10193                                              gint                y,
10194                                              GtkSelectionData   *data,
10195                                              guint               info,
10196                                              guint               time,
10197                                              gpointer            user_data)
10198 {
10199         GtkEditable *entry = (GtkEditable *)user_data;
10200         gchar *email = (gchar *)data->data;
10201
10202         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
10203          * does not work */
10204
10205         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
10206                 gchar *decoded=g_new(gchar, strlen(email));
10207                 int start = 0;
10208
10209                 email += strlen("mailto:");
10210                 decode_uri(decoded, email); /* will fit */
10211                 gtk_editable_delete_text(entry, 0, -1);
10212                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
10213                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
10214                 g_free(decoded);
10215                 return;
10216         }
10217         gtk_drag_finish(drag_context, TRUE, FALSE, time);
10218 }
10219
10220 static void compose_toggle_return_receipt_cb(GtkToggleAction *action, gpointer data)
10221 {
10222         Compose *compose = (Compose *)data;
10223
10224         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10225                 compose->return_receipt = TRUE;
10226         else
10227                 compose->return_receipt = FALSE;
10228 }
10229
10230 static void compose_toggle_remove_refs_cb(GtkToggleAction *action, gpointer data)
10231 {
10232         Compose *compose = (Compose *)data;
10233
10234         if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
10235                 compose->remove_references = TRUE;
10236         else
10237                 compose->remove_references = FALSE;
10238 }
10239
10240 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
10241                                             GdkEventKey *event,
10242                                             ComposeHeaderEntry *headerentry)
10243 {
10244         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
10245             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
10246             !(event->state & GDK_MODIFIER_MASK) &&
10247             (event->keyval == GDK_BackSpace) &&
10248             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
10249                 gtk_container_remove
10250                         (GTK_CONTAINER(headerentry->compose->header_table),
10251                          headerentry->combo);
10252                 gtk_container_remove
10253                         (GTK_CONTAINER(headerentry->compose->header_table),
10254                          headerentry->entry);
10255                 headerentry->compose->header_list =
10256                         g_slist_remove(headerentry->compose->header_list,
10257                                        headerentry);
10258                 g_free(headerentry);
10259         } else  if (event->keyval == GDK_Tab) {
10260                 if (headerentry->compose->header_last == headerentry) {
10261                         /* Override default next focus, and give it to subject_entry
10262                          * instead of notebook tabs
10263                          */
10264                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
10265                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
10266                         return TRUE;
10267                 }
10268         }
10269         return FALSE;
10270 }
10271
10272 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
10273                                     ComposeHeaderEntry *headerentry)
10274 {
10275         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
10276                 compose_create_header_entry(headerentry->compose);
10277                 g_signal_handlers_disconnect_matched
10278                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
10279                          0, 0, NULL, NULL, headerentry);
10280                 
10281                 /* Automatically scroll down */
10282                 compose_show_first_last_header(headerentry->compose, FALSE);
10283                 
10284         }
10285         return FALSE;
10286 }
10287
10288 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
10289 {
10290         GtkAdjustment *vadj;
10291
10292         g_return_if_fail(compose);
10293         g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
10294         g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
10295
10296         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
10297         gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
10298         gtk_adjustment_changed(vadj);
10299 }
10300
10301 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
10302                           const gchar *text, gint len, Compose *compose)
10303 {
10304         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
10305                                 (G_OBJECT(compose->text), "paste_as_quotation"));
10306         GtkTextMark *mark;
10307
10308         g_return_if_fail(text != NULL);
10309
10310         g_signal_handlers_block_by_func(G_OBJECT(buffer),
10311                                         G_CALLBACK(text_inserted),
10312                                         compose);
10313         if (paste_as_quotation) {
10314                 gchar *new_text;
10315                 const gchar *qmark;
10316                 guint pos = 0;
10317                 GtkTextIter start_iter;
10318
10319                 if (len < 0)
10320                         len = strlen(text);
10321
10322                 new_text = g_strndup(text, len);
10323
10324                 qmark = compose_quote_char_from_context(compose);
10325
10326                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10327                 gtk_text_buffer_place_cursor(buffer, iter);
10328
10329                 pos = gtk_text_iter_get_offset(iter);
10330
10331                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
10332                                                   _("Quote format error at line %d."));
10333                 quote_fmt_reset_vartable();
10334                 g_free(new_text);
10335                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
10336                                   GINT_TO_POINTER(paste_as_quotation - 1));
10337                                   
10338                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10339                 gtk_text_buffer_place_cursor(buffer, iter);
10340                 gtk_text_buffer_delete_mark(buffer, mark);
10341
10342                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
10343                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
10344                 compose_beautify_paragraph(compose, &start_iter, FALSE);
10345                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
10346                 gtk_text_buffer_delete_mark(buffer, mark);
10347         } else {
10348                 if (strcmp(text, "\n") || compose->automatic_break
10349                 || gtk_text_iter_starts_line(iter))
10350                         gtk_text_buffer_insert(buffer, iter, text, len);
10351                 else {
10352                         /* check if the preceding is just whitespace or quote */
10353                         GtkTextIter start_line;
10354                         gchar *tmp = NULL, *quote = NULL;
10355                         gint quote_len = 0, is_normal = 0;
10356                         start_line = *iter;
10357                         gtk_text_iter_set_line_offset(&start_line, 0); 
10358                         tmp = gtk_text_buffer_get_text(buffer, &start_line, iter, FALSE);
10359                         g_strstrip(tmp);
10360                         if (*tmp == '\0') {
10361                                 is_normal = 1;
10362                         } else {
10363                                 quote = compose_get_quote_str(buffer, &start_line, &quote_len);
10364                                 if (quote)
10365                                         is_normal = 1;
10366                                 g_free(quote);
10367                         }
10368                         g_free(tmp);
10369                         
10370                         if (is_normal) {
10371                                 gtk_text_buffer_insert(buffer, iter, text, len);
10372                         } else {
10373                                 gtk_text_buffer_insert_with_tags_by_name(buffer, 
10374                                         iter, text, len, "no_join", NULL);
10375                         }
10376                 }
10377         }
10378         
10379         if (!paste_as_quotation) {
10380                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
10381                 compose_beautify_paragraph(compose, iter, FALSE);
10382                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
10383                 gtk_text_buffer_delete_mark(buffer, mark);
10384         }
10385
10386         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
10387                                           G_CALLBACK(text_inserted),
10388                                           compose);
10389         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
10390
10391         if (prefs_common.autosave && 
10392             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
10393             compose->draft_timeout_tag != -2 /* disabled while loading */)
10394                 compose->draft_timeout_tag = g_timeout_add
10395                         (500, (GtkFunction) compose_defer_auto_save_draft, compose);
10396 }
10397 static gint compose_defer_auto_save_draft(Compose *compose)
10398 {
10399         compose->draft_timeout_tag = -1;
10400         compose_draft((gpointer)compose, COMPOSE_AUTO_SAVE);
10401         return FALSE;
10402 }
10403
10404 #if USE_ASPELL
10405 static void compose_check_all(GtkAction *action, gpointer data)
10406 {
10407         Compose *compose = (Compose *)data;
10408         if (compose->gtkaspell)
10409                 gtkaspell_check_all(compose->gtkaspell);
10410 }
10411
10412 static void compose_highlight_all(GtkAction *action, gpointer data)
10413 {
10414         Compose *compose = (Compose *)data;
10415         if (compose->gtkaspell)
10416                 gtkaspell_highlight_all(compose->gtkaspell);
10417 }
10418
10419 static void compose_check_backwards(GtkAction *action, gpointer data)
10420 {
10421         Compose *compose = (Compose *)data;
10422         if (compose->gtkaspell) 
10423                 gtkaspell_check_backwards(compose->gtkaspell);
10424         else {
10425                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10426         }
10427 }
10428
10429 static void compose_check_forwards_go(GtkAction *action, gpointer data)
10430 {
10431         Compose *compose = (Compose *)data;
10432         if (compose->gtkaspell) 
10433                 gtkaspell_check_forwards_go(compose->gtkaspell);
10434         else {
10435                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Spelling", FALSE);
10436         }
10437 }
10438 #endif
10439
10440 /*!
10441  *\brief        Guess originating forward account from MsgInfo and several 
10442  *              "common preference" settings. Return NULL if no guess. 
10443  */
10444 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10445 {
10446         PrefsAccount *account = NULL;
10447         
10448         g_return_val_if_fail(msginfo, NULL);
10449         g_return_val_if_fail(msginfo->folder, NULL);
10450         g_return_val_if_fail(msginfo->folder->prefs, NULL);
10451
10452         if (msginfo->folder->prefs->enable_default_account)
10453                 account = account_find_from_id(msginfo->folder->prefs->default_account);
10454                 
10455         if (!account) 
10456                 account = msginfo->folder->folder->account;
10457                 
10458         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10459                 gchar *to;
10460                 Xstrdup_a(to, msginfo->to, return NULL);
10461                 extract_address(to);
10462                 account = account_find_from_address(to, FALSE);
10463         }
10464
10465         if (!account && prefs_common.forward_account_autosel) {
10466                 gchar cc[BUFFSIZE];
10467                 if (!procheader_get_header_from_msginfo
10468                         (msginfo, cc,sizeof cc , "Cc:")) { 
10469                         gchar *buf = cc + strlen("Cc:");
10470                         extract_address(buf);
10471                         account = account_find_from_address(buf, FALSE);
10472                 }
10473         }
10474         
10475         if (!account && prefs_common.forward_account_autosel) {
10476                 gchar deliveredto[BUFFSIZE];
10477                 if (!procheader_get_header_from_msginfo
10478                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
10479                         gchar *buf = deliveredto + strlen("Delivered-To:");
10480                         extract_address(buf);
10481                         account = account_find_from_address(buf, FALSE);
10482                 }
10483         }
10484         
10485         return account;
10486 }
10487
10488 gboolean compose_close(Compose *compose)
10489 {
10490         gint x, y;
10491
10492         if (!g_mutex_trylock(compose->mutex)) {
10493                 /* we have to wait for the (possibly deferred by auto-save)
10494                  * drafting to be done, before destroying the compose under
10495                  * it. */
10496                 debug_print("waiting for drafting to finish...\n");
10497                 compose_allow_user_actions(compose, FALSE);
10498                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10499                 return FALSE;
10500         }
10501         g_return_val_if_fail(compose, FALSE);
10502         gtkut_widget_get_uposition(compose->window, &x, &y);
10503         prefs_common.compose_x = x;
10504         prefs_common.compose_y = y;
10505         g_mutex_unlock(compose->mutex);
10506         compose_destroy(compose);
10507         return FALSE;
10508 }
10509
10510 /**
10511  * Add entry field for each address in list.
10512  * \param compose     E-Mail composition object.
10513  * \param listAddress List of (formatted) E-Mail addresses.
10514  */
10515 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10516         GList *node;
10517         gchar *addr;
10518         node = listAddress;
10519         while( node ) {
10520                 addr = ( gchar * ) node->data;
10521                 compose_entry_append( compose, addr, COMPOSE_TO );
10522                 node = g_list_next( node );
10523         }
10524 }
10525
10526 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
10527                                     guint action, gboolean opening_multiple)
10528 {
10529         gchar *body = NULL;
10530         GSList *new_msglist = NULL;
10531         MsgInfo *tmp_msginfo = NULL;
10532         gboolean originally_enc = FALSE;
10533         Compose *compose = NULL;
10534
10535         g_return_if_fail(msgview != NULL);
10536
10537         g_return_if_fail(msginfo_list != NULL);
10538
10539         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10540                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10541                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10542
10543                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
10544                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10545                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10546                                                 orig_msginfo, mimeinfo);
10547                         if (tmp_msginfo != NULL) {
10548                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
10549
10550                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10551                                 tmp_msginfo->folder = orig_msginfo->folder;
10552                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
10553                                 if (orig_msginfo->tags)
10554                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10555                         }
10556                 }
10557         }
10558
10559         if (!opening_multiple)
10560                 body = messageview_get_selection(msgview);
10561
10562         if (new_msglist) {
10563                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10564                 procmsg_msginfo_free(tmp_msginfo);
10565                 g_slist_free(new_msglist);
10566         } else
10567                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10568
10569         if (compose && originally_enc) {
10570                 compose_force_encryption(compose, compose->account, FALSE);
10571         }
10572
10573         g_free(body);
10574 }
10575
10576 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
10577                                     guint action)
10578 {
10579         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
10580         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10581                 GSList *cur = msginfo_list;
10582                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10583                                                "messages. Opening the windows "
10584                                                "could take some time. Do you "
10585                                                "want to continue?"), 
10586                                                g_slist_length(msginfo_list));
10587                 if (g_slist_length(msginfo_list) > 9
10588                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10589                     != G_ALERTALTERNATE) {
10590                         g_free(msg);
10591                         return;
10592                 }
10593                 g_free(msg);
10594                 /* We'll open multiple compose windows */
10595                 /* let the WM place the next windows */
10596                 compose_force_window_origin = FALSE;
10597                 for (; cur; cur = cur->next) {
10598                         GSList tmplist;
10599                         tmplist.data = cur->data;
10600                         tmplist.next = NULL;
10601                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10602                 }
10603                 compose_force_window_origin = TRUE;
10604         } else {
10605                 /* forwarding multiple mails as attachments is done via a
10606                  * single compose window */
10607                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10608         }
10609 }
10610
10611 void compose_set_position(Compose *compose, gint pos)
10612 {
10613         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10614
10615         gtkut_text_view_set_position(text, pos);
10616 }
10617
10618 gboolean compose_search_string(Compose *compose,
10619                                 const gchar *str, gboolean case_sens)
10620 {
10621         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10622
10623         return gtkut_text_view_search_string(text, str, case_sens);
10624 }
10625
10626 gboolean compose_search_string_backward(Compose *compose,
10627                                 const gchar *str, gboolean case_sens)
10628 {
10629         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10630
10631         return gtkut_text_view_search_string_backward(text, str, case_sens);
10632 }
10633
10634 /* allocate a msginfo structure and populate its data from a compose data structure */
10635 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10636 {
10637         MsgInfo *newmsginfo;
10638         GSList *list;
10639         gchar buf[BUFFSIZE];
10640
10641         g_return_val_if_fail( compose != NULL, NULL );
10642
10643         newmsginfo = procmsg_msginfo_new();
10644
10645         /* date is now */
10646         get_rfc822_date(buf, sizeof(buf));
10647         newmsginfo->date = g_strdup(buf);
10648
10649         /* from */
10650         if (compose->from_name) {
10651                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10652                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10653         }
10654
10655         /* subject */
10656         if (compose->subject_entry)
10657                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10658
10659         /* to, cc, reply-to, newsgroups */
10660         for (list = compose->header_list; list; list = list->next) {
10661                 gchar *header = gtk_editable_get_chars(
10662                                                                 GTK_EDITABLE(
10663                                                                 gtk_bin_get_child(GTK_BIN((((ComposeHeaderEntry *)list->data)->combo)))), 0, -1);
10664                 gchar *entry = gtk_editable_get_chars(
10665                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10666
10667                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10668                         if ( newmsginfo->to == NULL ) {
10669                                 newmsginfo->to = g_strdup(entry);
10670                         } else if (entry && *entry) {
10671                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10672                                 g_free(newmsginfo->to);
10673                                 newmsginfo->to = tmp;
10674                         }
10675                 } else
10676                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10677                         if ( newmsginfo->cc == NULL ) {
10678                                 newmsginfo->cc = g_strdup(entry);
10679                         } else if (entry && *entry) {
10680                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10681                                 g_free(newmsginfo->cc);
10682                                 newmsginfo->cc = tmp;
10683                         }
10684                 } else
10685                 if ( strcasecmp(header,
10686                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10687                         if ( newmsginfo->newsgroups == NULL ) {
10688                                 newmsginfo->newsgroups = g_strdup(entry);
10689                         } else if (entry && *entry) {
10690                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10691                                 g_free(newmsginfo->newsgroups);
10692                                 newmsginfo->newsgroups = tmp;
10693                         }
10694                 }
10695
10696                 g_free(header);
10697                 g_free(entry);  
10698         }
10699
10700         /* other data is unset */
10701
10702         return newmsginfo;
10703 }
10704
10705 #ifdef USE_ASPELL
10706 /* update compose's dictionaries from folder dict settings */
10707 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10708                                                 FolderItem *folder_item)
10709 {
10710         g_return_if_fail(compose != NULL);
10711
10712         if (compose->gtkaspell && folder_item && folder_item->prefs) {
10713                 FolderItemPrefs *prefs = folder_item->prefs;
10714
10715                 if (prefs->enable_default_dictionary)
10716                         gtkaspell_change_dict(compose->gtkaspell,
10717                                         prefs->default_dictionary, FALSE);
10718                 if (folder_item->prefs->enable_default_alt_dictionary)
10719                         gtkaspell_change_alt_dict(compose->gtkaspell,
10720                                         prefs->default_alt_dictionary);
10721                 if (prefs->enable_default_dictionary
10722                         || prefs->enable_default_alt_dictionary)
10723                         compose_spell_menu_changed(compose);
10724         }
10725 }
10726 #endif
10727
10728 /*
10729  * End of Source.
10730  */