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