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