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