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