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