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