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