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